From 57ac1536718aebeed2aaeda880f07e43cea36d24 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sun, 26 Apr 2020 19:06:59 +0100 Subject: [PATCH 001/118] docs: initial re-write of all documentation --- docs/building.md | 22 ++- docs/changelog.md | 29 ++++ docs/components/emummc.md | 4 + docs/components/exosphere.md | 22 ++- docs/components/fusee.md | 22 +++ docs/components/fusee/BCT.md | 73 ---------- docs/components/fusee/fusee.md | 20 --- docs/components/libraries.md | 11 ++ docs/components/mesosphere.md | 3 + docs/components/modules/ams_mitm.md | 33 +++++ docs/components/modules/boot.md | 4 + docs/components/modules/boot2.md | 4 + docs/components/modules/creport.md | 4 + docs/components/modules/dmnt.md | 42 ++++++ docs/components/modules/eclct.stub.md | 4 + docs/components/modules/erpt.md | 4 + docs/components/modules/fatal.md | 4 + docs/components/modules/jpegdec.md | 4 + docs/components/modules/loader.md | 77 ++++++++++ docs/components/modules/ncm.md | 4 + docs/components/modules/pgl.md | 2 + docs/components/modules/pm.md | 30 ++++ docs/components/modules/ro.md | 14 ++ docs/components/modules/sm.md | 47 ++++++ docs/components/modules/spl.md | 2 + docs/components/{fusee => }/sept.md | 11 +- docs/components/stratosphere.md | 27 ++-- docs/components/thermosphere.md | 5 +- docs/components/troposphere.md | 5 +- docs/{ => features}/cheats.md | 199 ++++++++++++++++++-------- docs/features/configurations.md | 141 ++++++++++++++++++ docs/flags.md | 12 -- docs/main.md | 45 +++--- docs/modules/boot.md | 2 - docs/modules/creport.md | 2 - docs/modules/dmnt.md | 38 ----- docs/modules/fs_mitm.md | 2 - docs/modules/loader.md | 126 ---------------- docs/modules/pm.md | 23 --- docs/modules/set_mitm.md | 79 ---------- docs/modules/sm.md | 125 ---------------- docs/roadmap.md | 10 -- 42 files changed, 711 insertions(+), 626 deletions(-) create mode 100644 docs/components/emummc.md create mode 100644 docs/components/fusee.md delete mode 100644 docs/components/fusee/BCT.md delete mode 100644 docs/components/fusee/fusee.md create mode 100644 docs/components/libraries.md create mode 100644 docs/components/mesosphere.md create mode 100644 docs/components/modules/ams_mitm.md create mode 100644 docs/components/modules/boot.md create mode 100644 docs/components/modules/boot2.md create mode 100644 docs/components/modules/creport.md create mode 100644 docs/components/modules/dmnt.md create mode 100644 docs/components/modules/eclct.stub.md create mode 100644 docs/components/modules/erpt.md create mode 100644 docs/components/modules/fatal.md create mode 100644 docs/components/modules/jpegdec.md create mode 100644 docs/components/modules/loader.md create mode 100644 docs/components/modules/ncm.md create mode 100644 docs/components/modules/pgl.md create mode 100644 docs/components/modules/pm.md create mode 100644 docs/components/modules/ro.md create mode 100644 docs/components/modules/sm.md create mode 100644 docs/components/modules/spl.md rename docs/components/{fusee => }/sept.md (56%) rename docs/{ => features}/cheats.md (70%) create mode 100644 docs/features/configurations.md delete mode 100644 docs/flags.md delete mode 100644 docs/modules/boot.md delete mode 100644 docs/modules/creport.md delete mode 100644 docs/modules/dmnt.md delete mode 100644 docs/modules/fs_mitm.md delete mode 100644 docs/modules/loader.md delete mode 100644 docs/modules/pm.md delete mode 100644 docs/modules/set_mitm.md delete mode 100644 docs/modules/sm.md diff --git a/docs/building.md b/docs/building.md index f3574ba36..7c8cfbf95 100644 --- a/docs/building.md +++ b/docs/building.md @@ -1,8 +1,22 @@ # 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) ++ [PyCryptodome](https://pypi.org/project/pycryptodome) -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 +Follow the guide located [here](https://switchbrew.org/wiki/Setting_up_Development_Environment) 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`. +Install the following packages via (dkp-)pacman: ++ switch-dev ++ switch-freetype ++ switch-libjpeg-turbo ++ devkitARM ++ devkitarm-rules + +In order to build [Sept](components/sept.md) the pycryptodome PyPi package is required. You may install this package by running `pip install pycryptodome` under the installed Python environment of your choice. You may also want to install the zip package to support the `make dist` recipe. + +Finally, clone the Atmosphère repository and run `make` under its root directory. diff --git a/docs/changelog.md b/docs/changelog.md index 8a243e354..9f43dfdad 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,5 @@ # Changelog + ## 0.12.0 + Configuration for exosphere was moved to sd:/exosphere.ini. + This is to facilitate BIS protection changes described below. @@ -47,6 +48,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 +56,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 +80,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 +93,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 +116,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 +131,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 +166,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 +195,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 +260,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 +280,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 +304,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 +328,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 +358,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 +375,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 +401,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 +409,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 +425,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 +463,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 +485,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 +501,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 +522,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 +532,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 +554,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 +588,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 +597,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 +618,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 ` (AMS ..)`. + 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. diff --git a/docs/components/emummc.md b/docs/components/emummc.md new file mode 100644 index 000000000..f7d45f462 --- /dev/null +++ b/docs/components/emummc.md @@ -0,0 +1,4 @@ +# emummc +emummc is a collaboration project that provides eMMC storage emulation. + +Please refer to the project's repository [here](https://github.com/m4xw/emuMMC) for detailed instructions and documentation. diff --git a/docs/components/exosphere.md b/docs/components/exosphere.md index b8a1103c8..41eee173b 100644 --- a/docs/components/exosphere.md +++ b/docs/components/exosphere.md @@ -1,10 +1,18 @@ # 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 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: ++ smc_ams_iram_copy ++ smc_ams_write_address ++ smc_ams_get_emummc_config + +## 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. diff --git a/docs/components/fusee.md b/docs/components/fusee.md new file mode 100644 index 000000000..9a47bedd4 --- /dev/null +++ b/docs/components/fusee.md @@ -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/BCT.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. diff --git a/docs/components/fusee/BCT.md b/docs/components/fusee/BCT.md deleted file mode 100644 index 1b6b63f58..000000000 --- a/docs/components/fusee/BCT.md +++ /dev/null @@ -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 -``` diff --git a/docs/components/fusee/fusee.md b/docs/components/fusee/fusee.md deleted file mode 100644 index 64c9575bd..000000000 --- a/docs/components/fusee/fusee.md +++ /dev/null @@ -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. diff --git a/docs/components/libraries.md b/docs/components/libraries.md new file mode 100644 index 000000000..06b13d23d --- /dev/null +++ b/docs/components/libraries.md @@ -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. diff --git a/docs/components/mesosphere.md b/docs/components/mesosphere.md new file mode 100644 index 000000000..5ee758fa1 --- /dev/null +++ b/docs/components/mesosphere.md @@ -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. diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md new file mode 100644 index 000000000..a186de336 --- /dev/null +++ b/docs/components/modules/ams_mitm.md @@ -0,0 +1,33 @@ +# 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) ++ Homebrew Loader (to allow homebrew to take advantage of the feature) + +## fs_mitm +fs_mitm enables intercepting file system operations. It can log, 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 currently intercepts: ++ Homebrew Loader (to help homebrew not need to be recompiled due to a breaking change introduced in the past) + +## ns_mitm +ns_mitm enables intercepting requests to application control services. It currently intercepts: ++ Web Applets (to facilitate hbl 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 settings requests + +### 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 #.#.#)`. This allows users to easily verify what version of Atmosphère 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. diff --git a/docs/components/modules/boot.md b/docs/components/modules/boot.md new file mode 100644 index 000000000..6612184e1 --- /dev/null +++ b/docs/components/modules/boot.md @@ -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. diff --git a/docs/components/modules/boot2.md b/docs/components/modules/boot2.md new file mode 100644 index 000000000..c07b861ab --- /dev/null +++ b/docs/components/modules/boot2.md @@ -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. diff --git a/docs/components/modules/creport.md b/docs/components/modules/creport.md new file mode 100644 index 000000000..8e61c465f --- /dev/null +++ b/docs/components/modules/creport.md @@ -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. diff --git a/docs/components/modules/dmnt.md b/docs/components/modules/dmnt.md new file mode 100644 index 000000000..c65c295c3 --- /dev/null +++ b/docs/components/modules/dmnt.md @@ -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 out; + [65001] GetCheatProcessEvent() -> sf::OutCopyHandle out_event; + [65002] GetCheatProcessMetadata() -> sf::Out out_metadata; + [65003] ForceOpenCheatProcess(); + [65004] PauseCheatProcess(); + [65005] ResumeCheatProcess(); + + [65100] GetCheatProcessMappingCount() -> sf::Out out_count; + [65101] GetCheatProcessMappings(u64 offset) -> sf::OutArray &mappings, sf::Out 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 mapping; + + [65200] GetCheatCount() -> sf::Out out_count; + [65201] GetCheats(u64 offset) -> sf::OutArray &cheats, sf::Out out_count; + [65202] GetCheatById(u32 cheat_id) -> sf::Out cheat; + [65203] ToggleCheat(u32 cheat_id); + [65204] AddCheat(CheatDefinition &cheat, bool enabled) -> sf::Out out_cheat_id; + [65205] RemoveCheat(u32 cheat_id); + [65206] ReadStaticRegister(u8 which) -> sf::Out out; + [65207] WriteStaticRegister(u8 which, u64 value); + [65208] ResetStaticRegisters(); + + [65300] GetFrozenAddressCount() -> sf::Out out_count; + [65301] GetFrozenAddresses(u64 offset) ->sf::OutArray &addresses, sf::Out out_count; + [65302] GetFrozenAddress(u64 address) -> sf::Out entry; + [65303] EnableFrozenAddress(u64 address, u64 width) -> sf::Out out_value; + [65304] DisableFrozenAddress(u64 address); +} +``` diff --git a/docs/components/modules/eclct.stub.md b/docs/components/modules/eclct.stub.md new file mode 100644 index 000000000..0993ff026 --- /dev/null +++ b/docs/components/modules/eclct.stub.md @@ -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. diff --git a/docs/components/modules/erpt.md b/docs/components/modules/erpt.md new file mode 100644 index 000000000..ffaeb648d --- /dev/null +++ b/docs/components/modules/erpt.md @@ -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/`. diff --git a/docs/components/modules/fatal.md b/docs/components/modules/fatal.md new file mode 100644 index 000000000..556cd0e42 --- /dev/null +++ b/docs/components/modules/fatal.md @@ -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/` (if a crash report was not generated). diff --git a/docs/components/modules/jpegdec.md b/docs/components/modules/jpegdec.md new file mode 100644 index 000000000..d04403472 --- /dev/null +++ b/docs/components/modules/jpegdec.md @@ -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. diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md new file mode 100644 index 000000000..174e9978b --- /dev/null +++ b/docs/components/modules/loader.md @@ -0,0 +1,77 @@ +# 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 +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//exefs/ +``` + +This allows the replacement of applets, system modules, 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 +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//.ips +``` +This organization allows patch sets affecting multiple NSOs 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 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. + +### HBL Support +Atmosphère provides first class support for [hbmenu](https://github.com/switchbrew/nx-hbmenu/releases) and [hbloader](https://github.com/switchbrew/nx-hbloader/releases). + +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 out; + [65001] AtmosphereGetProgramInfo(ncm::ProgramLocation &loc) -> sf::Out out_program_info, sf::Out out_status; + [65002] AtmospherePinProgram(ncm::ProgramLocation &loc, cfg::OverrideStatus &override_status) -> sf::Out 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 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); +} +``` diff --git a/docs/components/modules/ncm.md b/docs/components/modules/ncm.md new file mode 100644 index 000000000..f9ef25019 --- /dev/null +++ b/docs/components/modules/ncm.md @@ -0,0 +1,4 @@ +# ncm +This module is a reimplementation of the Horizon OS's "ncm" system module, which is responsible content management. + +Atmosphère's reimplementation is currently opt-in only. See [here](../../features/configurations.md) for more information. diff --git a/docs/components/modules/pgl.md b/docs/components/modules/pgl.md new file mode 100644 index 000000000..bc8eaed77 --- /dev/null +++ b/docs/components/modules/pgl.md @@ -0,0 +1,2 @@ +# pgl +This module is a reimplementation of the Horizon OS's "pgl" system module, which is responsible for launching programs. diff --git a/docs/components/modules/pm.md b/docs/components/modules/pm.md new file mode 100644 index 000000000..9681adde5 --- /dev/null +++ b/docs/components/modules/pm.md @@ -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 out_loc, sf::Out out_status; + [65001] AtmosphereGetCurrentLimitInfo(u32 group, u32 resource) -> sf::Out out_cur_val, sf::Out 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 out; + [65001] AtmosphereHasLaunchedProgram(ncm::ProgramId program_id) -> sf::Out out; + [65002] AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::Out out_loc, sf::Out 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. diff --git a/docs/components/modules/ro.md b/docs/components/modules/ro.md new file mode 100644 index 000000000..b0389dc39 --- /dev/null +++ b/docs/components/modules/ro.md @@ -0,0 +1,14 @@ +# ro +This module is a reimplementation of the Horizon OS's "ro" system module, which is responsible for loading dynamic libraries. + +## 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//.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. diff --git a/docs/components/modules/sm.md b/docs/components/modules/sm.md new file mode 100644 index 000000000..1c709c9ea --- /dev/null +++ b/docs/components/modules/sm.md @@ -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 record; + [65001] AtmosphereListRecords(u64 offset) -> sf::OutArray &records, sf::Out out_count; + [65002] AtmosphereGetRecordSize() -> sf::Out 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 client_info, sf::OutMoveHandle fwd_h; + [65004] AtmosphereHasMitm(ServiceName service) -> sf::Out out; + [65005] AtmosphereWaitMitm(ServiceName service); + [65006] AtmosphereDeclareFutureMitm(ServiceName service); + + [65100] AtmosphereHasService(ServiceName service) -> sf::Out 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 out; + [65002] AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, sf::InBuffer &acid_sac, sf::InBuffer &aci_sac); +} +``` diff --git a/docs/components/modules/spl.md b/docs/components/modules/spl.md new file mode 100644 index 000000000..72d5c3a5e --- /dev/null +++ b/docs/components/modules/spl.md @@ -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. diff --git a/docs/components/fusee/sept.md b/docs/components/sept.md similarity index 56% rename from docs/components/fusee/sept.md rename to docs/components/sept.md index 6d15be23e..b080cba7c 100644 --- a/docs/components/fusee/sept.md +++ b/docs/components/sept.md @@ -1,15 +1,14 @@ -# sept +# 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`. +## 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 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. +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. diff --git a/docs/components/stratosphere.md b/docs/components/stratosphere.md index 58813ab77..e539b887d 100644 --- a/docs/components/stratosphere.md +++ b/docs/components/stratosphere.md @@ -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 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) diff --git a/docs/components/thermosphere.md b/docs/components/thermosphere.md index 6b90525e3..c12f7c85d 100644 --- a/docs/components/thermosphere.md +++ b/docs/components/thermosphere.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 is a work in progress hypervisor implementation. +This aims to provide functionality at the EL2 level which remains unused by the Horizon OS. diff --git a/docs/components/troposphere.md b/docs/components/troposphere.md index 2664843d8..538bc8d88 100644 --- a/docs/components/troposphere.md +++ b/docs/components/troposphere.md @@ -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 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. diff --git a/docs/cheats.md b/docs/features/cheats.md similarity index 70% rename from docs/cheats.md rename to docs/features/cheats.md index 5c4bd3f53..68f0ab0b0 100644 --- a/docs/cheats.md +++ b/docs/features/cheats.md @@ -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//cheats/.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//cheats/.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. 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,119 @@ 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 diff --git a/docs/features/configurations.md b/docs/features/configurations.md new file mode 100644 index 000000000..3abac4920 --- /dev/null +++ b/docs/features/configurations.md @@ -0,0 +1,141 @@ +# 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 +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. +``` + +### 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: +``` +enable_ncm = 1 +``` + +### Logging +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 +``` + +## 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. + +## 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. +``` +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 +Settings are 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 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. + +## 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` inside `/atmosphere/contents//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. diff --git a/docs/flags.md b/docs/flags.md deleted file mode 100644 index 5ee403b28..000000000 --- a/docs/flags.md +++ /dev/null @@ -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` inside `/atmosphere/titles//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` 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. \ No newline at end of file diff --git a/docs/main.md b/docs/main.md index 02f3efc39..c6bd497e3 100644 --- a/docs/main.md +++ b/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). diff --git a/docs/modules/boot.md b/docs/modules/boot.md deleted file mode 100644 index 53013b32c..000000000 --- a/docs/modules/boot.md +++ /dev/null @@ -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. diff --git a/docs/modules/creport.md b/docs/modules/creport.md deleted file mode 100644 index a6a996f43..000000000 --- a/docs/modules/creport.md +++ /dev/null @@ -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. diff --git a/docs/modules/dmnt.md b/docs/modules/dmnt.md deleted file mode 100644 index 84e7410ce..000000000 --- a/docs/modules/dmnt.md +++ /dev/null @@ -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, u64 count; - [65102] ReadCheatProcessMemory(u64 address, u64 size) -> buffer data; - [65103] WriteCheatProcessMemory(u64 address, u64 size, buffer data); - [65104] QueryCheatProcessMemory(u64 address) -> MemoryInfo; - - [65200] GetCheatCount() -> u64; - [65201] GetCheats(u64 offset) -> buffer, u64 count; - [65202] GetCheatById(u32 cheat_id) -> buffer cheat; - [65203] ToggleCheat(u32 cheat_id); - [65204] AddCheat(buffer cheat, bool enabled) -> u32 cheat_id; - [65203] RemoveCheat(u32 cheat_id); - - [65300] GetFrozenAddressCount() -> u64; - [65301] GetFrozenAddresses(u64 offset) -> buffer, u64 count; - [65302] GetFrozenAddress(u64 address) -> FrozenAddressEntry; - [65303] EnableFrozenAddress(u64 address, u64 width) -> u64 value; - [65304] DisableFrozenAddress(u64 address); -} -``` diff --git a/docs/modules/fs_mitm.md b/docs/modules/fs_mitm.md deleted file mode 100644 index 8f1e85cf1..000000000 --- a/docs/modules/fs_mitm.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/docs/modules/loader.md b/docs/modules/loader.md deleted file mode 100644 index c1bcc5bd2..000000000 --- a/docs/modules/loader.md +++ /dev/null @@ -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//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); -} -``` diff --git a/docs/modules/pm.md b/docs/modules/pm.md deleted file mode 100644 index 288fdf355..000000000 --- a/docs/modules/pm.md +++ /dev/null @@ -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. diff --git a/docs/modules/set_mitm.md b/docs/modules/set_mitm.md deleted file mode 100644 index 495329b4e..000000000 --- a/docs/modules/set_mitm.md +++ /dev/null @@ -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 -``` diff --git a/docs/modules/sm.md b/docs/modules/sm.md deleted file mode 100644 index e92d160a7..000000000 --- a/docs/modules/sm.md +++ /dev/null @@ -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. diff --git a/docs/roadmap.md b/docs/roadmap.md index 03367dff9..97ee82dca 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -2,15 +2,5 @@ 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. From 59ea93e9cd73f49912fb7baf2fcf50af1388556d Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 19:18:49 +0100 Subject: [PATCH 002/118] docs: style fixes --- docs/components/modules/ams_mitm.md | 6 +++--- docs/components/modules/boot.md | 2 +- docs/components/modules/boot2.md | 2 +- docs/components/modules/creport.md | 2 +- docs/components/modules/dmnt.md | 2 +- docs/components/modules/eclct.stub.md | 2 +- docs/components/modules/erpt.md | 2 +- docs/components/modules/fatal.md | 2 +- docs/components/modules/jpegdec.md | 2 +- docs/components/modules/loader.md | 2 +- docs/components/modules/ncm.md | 2 +- docs/components/modules/pgl.md | 2 +- docs/components/modules/pm.md | 2 +- docs/components/modules/ro.md | 2 +- docs/components/modules/sm.md | 2 +- docs/components/modules/spl.md | 2 +- docs/features/cheats.md | 20 +++++--------------- 17 files changed, 23 insertions(+), 33 deletions(-) diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index a186de336..90f143067 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -3,8 +3,8 @@ This module provides methods to intercept services provided by other system modu ## 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) ++ `am` system module (to intercept the Reboot/Power buttons in the overlay menu) ++ `fatal` system module (to simplify payload reboot logic significantly) + Homebrew Loader (to allow homebrew to take advantage of the feature) ## fs_mitm @@ -20,7 +20,7 @@ ns_mitm enables intercepting requests to application control services. It curren ## 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) ++ `ns` system module and games (to allow for overriding game locales) + All settings requests ### Firmware Version diff --git a/docs/components/modules/boot.md b/docs/components/modules/boot.md index 6612184e1..c0005584b 100644 --- a/docs/components/modules/boot.md +++ b/docs/components/modules/boot.md @@ -1,4 +1,4 @@ # boot -This module is a reimplementation of the Horizon OS's "boot" system module, which is responsible for initializing and configuring hardware. +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. diff --git a/docs/components/modules/boot2.md b/docs/components/modules/boot2.md index c07b861ab..4b7e6f995 100644 --- a/docs/components/modules/boot2.md +++ b/docs/components/modules/boot2.md @@ -1,4 +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. +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. diff --git a/docs/components/modules/creport.md b/docs/components/modules/creport.md index 8e61c465f..0517840de 100644 --- a/docs/components/modules/creport.md +++ b/docs/components/modules/creport.md @@ -1,4 +1,4 @@ # creport -This module is a reimplementation of the Horizon OS's "creport" system module, which is responsible for managing crash reports. +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. diff --git a/docs/components/modules/dmnt.md b/docs/components/modules/dmnt.md index c65c295c3..aa9f5fa71 100644 --- a/docs/components/modules/dmnt.md +++ b/docs/components/modules/dmnt.md @@ -1,5 +1,5 @@ # dmnt -This module is a reimplementation of the Horizon OS's "dmnt" system module, which provides a debug monitor. +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. diff --git a/docs/components/modules/eclct.stub.md b/docs/components/modules/eclct.stub.md index 0993ff026..c33df9e36 100644 --- a/docs/components/modules/eclct.stub.md +++ b/docs/components/modules/eclct.stub.md @@ -1,4 +1,4 @@ # eclct.stub -This module is a reimplementation of the Horizon OS's "eclct" system module, which collects error reports. +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. diff --git a/docs/components/modules/erpt.md b/docs/components/modules/erpt.md index ffaeb648d..dd8e459f6 100644 --- a/docs/components/modules/erpt.md +++ b/docs/components/modules/erpt.md @@ -1,4 +1,4 @@ # erpt -This module is a reimplementation of the Horizon OS's "erpt" system module, which is responsible for managing error reports. +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/`. diff --git a/docs/components/modules/fatal.md b/docs/components/modules/fatal.md index 556cd0e42..3a17c94d0 100644 --- a/docs/components/modules/fatal.md +++ b/docs/components/modules/fatal.md @@ -1,4 +1,4 @@ # fatal -This module is a reimplementation of the Horizon OS's "fatal" system module, which is responsible for managing fatal reports. +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/` (if a crash report was not generated). diff --git a/docs/components/modules/jpegdec.md b/docs/components/modules/jpegdec.md index d04403472..fcc0d3abe 100644 --- a/docs/components/modules/jpegdec.md +++ b/docs/components/modules/jpegdec.md @@ -1,4 +1,4 @@ # jpegdec -This module is a reimplementation of the Horizon OS's "jpegdec" system module, which is responsible for JPEG format decoding. +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. diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md index 174e9978b..b4e363cae 100644 --- a/docs/components/modules/loader.md +++ b/docs/components/modules/loader.md @@ -1,5 +1,5 @@ # 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. +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. diff --git a/docs/components/modules/ncm.md b/docs/components/modules/ncm.md index f9ef25019..5a4971d91 100644 --- a/docs/components/modules/ncm.md +++ b/docs/components/modules/ncm.md @@ -1,4 +1,4 @@ # ncm -This module is a reimplementation of the Horizon OS's "ncm" system module, which is responsible content management. +This module is a reimplementation of the Horizon OS's `ncm` system module, which is responsible content management. Atmosphère's reimplementation is currently opt-in only. See [here](../../features/configurations.md) for more information. diff --git a/docs/components/modules/pgl.md b/docs/components/modules/pgl.md index bc8eaed77..fa1658a8d 100644 --- a/docs/components/modules/pgl.md +++ b/docs/components/modules/pgl.md @@ -1,2 +1,2 @@ # pgl -This module is a reimplementation of the Horizon OS's "pgl" system module, which is responsible for launching programs. +This module is a reimplementation of the Horizon OS's `pgl` system module, which is responsible for launching programs. diff --git a/docs/components/modules/pm.md b/docs/components/modules/pm.md index 9681adde5..cc52a460d 100644 --- a/docs/components/modules/pm.md +++ b/docs/components/modules/pm.md @@ -1,5 +1,5 @@ # 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. +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. diff --git a/docs/components/modules/ro.md b/docs/components/modules/ro.md index b0389dc39..9f6f7d767 100644 --- a/docs/components/modules/ro.md +++ b/docs/components/modules/ro.md @@ -1,5 +1,5 @@ # ro -This module is a reimplementation of the Horizon OS's "ro" system module, which is responsible for loading dynamic libraries. +This module is a reimplementation of the Horizon OS's `ro` system module, which is responsible for loading dynamic libraries. ## Extensions Atmosphère extends this module to allow libraries to be patched by files stored on the SD card. diff --git a/docs/components/modules/sm.md b/docs/components/modules/sm.md index 1c709c9ea..bba1712d8 100644 --- a/docs/components/modules/sm.md +++ b/docs/components/modules/sm.md @@ -1,5 +1,5 @@ # sm -This module is a reimplementation of the Horizon OS's "sm" system module, which is responsible for service management. +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. diff --git a/docs/components/modules/spl.md b/docs/components/modules/spl.md index 72d5c3a5e..ec1605a40 100644 --- a/docs/components/modules/spl.md +++ b/docs/components/modules/spl.md @@ -1,2 +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. +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. diff --git a/docs/features/cheats.md b/docs/features/cheats.md index 68f0ab0b0..007a50584 100644 --- a/docs/features/cheats.md +++ b/docs/features/cheats.md @@ -333,9 +333,7 @@ C0TcS5X0 Code type 0xC1 performs saving or restoring of registers. #### Encoding -``` -C10D0Sx0 -``` +`C10D0Sx0` + D: Destination index. + S: Source index. @@ -353,9 +351,7 @@ C10D0Sx0 Code type 0xC2 performs saving or restoring of multiple registers using a bitmask. #### Encoding -``` -C2x0XXXX -``` +`C2x0XXXX` + x: Operand Type, see below. + X: 16-bit bitmask, bit i == save or restore register i. @@ -372,9 +368,7 @@ C2x0XXXX Code type 0xC3 reads or writes a static register with a given register. #### Encoding -``` -C3000XXx -``` +`C3000XXx` + XX: Static register index, 0x00 to 0x7F for reading or 0x80 to 0xFF for writing. + x: Register index. @@ -392,9 +386,7 @@ This reserves an additional 16 opcodes for future use. Code type 0xFF0 pauses the current process. #### Encoding -``` -FF0????? -``` +`FF0?????` --- @@ -402,9 +394,7 @@ FF0????? Code type 0xFF1 resumes the current process. #### Encoding -``` -FF1????? -``` +`FF1?????` --- From 96229120590f299c58d40cd6db1f46f1aab01e35 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 20:43:32 +0100 Subject: [PATCH 003/118] docs: remove switch-freetype dependency --- docs/building.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 7c8cfbf95..5e5449c29 100644 --- a/docs/building.md +++ b/docs/building.md @@ -12,7 +12,6 @@ Follow the guide located [here](https://switchbrew.org/wiki/Setting_up_Developme Install the following packages via (dkp-)pacman: + switch-dev -+ switch-freetype + switch-libjpeg-turbo + devkitARM + devkitarm-rules From a61fdc8d6587797bf517685550a1c29408ef05f3 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 20:49:14 +0100 Subject: [PATCH 004/118] docs: correct "nogc" description --- docs/features/configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/configurations.md b/docs/features/configurations.md index 3abac4920..b296de6fe 100644 --- a/docs/features/configurations.md +++ b/docs/features/configurations.md @@ -15,7 +15,7 @@ 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. +"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: ``` From 93e855a293fcc12915ebfa5e135e73fe25af1ef5 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 20:56:12 +0100 Subject: [PATCH 005/118] docs: document exefs PFS0 replacement --- docs/components/modules/loader.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md index b4e363cae..97204bee4 100644 --- a/docs/components/modules/loader.md +++ b/docs/components/modules/loader.md @@ -28,6 +28,11 @@ This allows the replacement of applets, system modules, or even games with homeb 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. +It is also 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 +``` + ### 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. ``` From a9cc74da343adca6d89a70b82a8efb1d094ca828 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 20:59:29 +0100 Subject: [PATCH 006/118] docs: remove misleading sentence in fatal module --- docs/components/modules/fatal.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components/modules/fatal.md b/docs/components/modules/fatal.md index 3a17c94d0..63e8ed1a2 100644 --- a/docs/components/modules/fatal.md +++ b/docs/components/modules/fatal.md @@ -1,4 +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/` (if a crash report was not generated). +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/`. From faaef5eff502ea79d6e7e5720e1821eb92a08f84 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 26 Apr 2020 21:01:54 +0100 Subject: [PATCH 007/118] docs: hid_mitm is disabled by default --- docs/components/modules/ams_mitm.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index 90f143067..e95dfd1a5 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -8,10 +8,10 @@ bpc_mitm enables intercepting requests to power control services. It currently i + Homebrew Loader (to allow homebrew to take advantage of the feature) ## fs_mitm -fs_mitm enables intercepting file system operations. It can log, deny, delay, replace, or redirect any request made to the file system. It enables LayeredFS to function, which allows for replacement of game assets. +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 currently intercepts: +hid_mitm enables intercepting requests to controller device services. It is currently disabled by default. If enabled, it intercepts: + Homebrew Loader (to help homebrew not need to be recompiled due to a breaking change introduced in the past) ## ns_mitm From aca2992c687bbe8325e4f23fbab07e3b49ad2e6c Mon Sep 17 00:00:00 2001 From: SciresM <Sciresm@gmail.com> Date: Mon, 27 Apr 2020 09:35:23 -0700 Subject: [PATCH 008/118] Add up-to-date roadmap. --- docs/roadmap.md | 53 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 97ee82dca..210e3b624 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +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). -+ 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 in late April of 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**: Under active development by SciresM +* **Estimated Time**: May 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 + +## exosphere re-write +* **Description**: exosphère, atmosphère's reimplementation of Horizon's Secure Monitor, 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. In addition, exosphère was written to conform to constraints that no longer apply in an environment where it is not launched from the web browser, and where using a custom firmware image to orchestrate wake-from-sleep is possible. exosphère currently uses all but 1 KB of the space available to it, putting it at risk of breaking as future firmware updates are supported. A re-write will solve these issues. +* **Development Status**: Planned. +* **Estimated Time**: 2020-2021. + +## 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. From 2dfe5b192eebe837f06866fbe92b44209cd4b913 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 27 Apr 2020 10:33:51 -0700 Subject: [PATCH 009/118] result: add R_CATCH_MODULE --- .../libvapours/include/vapours/results/results_common.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/libvapours/include/vapours/results/results_common.hpp b/libraries/libvapours/include/vapours/results/results_common.hpp index 1fb735f88..0e2fecc97 100644 --- a/libraries/libvapours/include/vapours/results/results_common.hpp +++ b/libraries/libvapours/include/vapours/results/results_common.hpp @@ -289,6 +289,10 @@ namespace ams::result::impl { } else if (::ams::result::impl::AnyIncludes<__VA_ARGS__>(R_CURRENT_RESULT)) { \ if (true) +#define R_CATCH_MODULE(__module__) \ + } else if ((R_CURRENT_RESULT).GetModule() == ::ams::R_NAMESPACE_MODULE_ID(__module__)) { \ + if (true) + #define R_CONVERT(catch_type, convert_type) \ R_CATCH(catch_type) { return static_cast<::ams::Result>(convert_type); } From 0bfbc6e6ebae0e256d91546c703d26e60137cff8 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 27 Apr 2020 10:34:30 -0700 Subject: [PATCH 010/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "6913aa52" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "6913aa52" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index 3c7fdcf06..e71fbde40 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = bb40dae329450407a24db2c3c4edd4ed1fd46532 - parent = 745887955507cf42361f37aacfe5ca5477b8e5d5 + commit = 6913aa52953f228f7abc7cc7617a6ae6baec1eca + parent = 2dfe5b192eebe837f06866fbe92b44209cd4b913 method = merge cmdver = 0.4.1 From 234c83522a091a46619b31bb174872d08b5dbc91 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Mon, 27 Apr 2020 18:40:22 +0100 Subject: [PATCH 011/118] docs: address review commentary --- docs/building.md | 26 ++++++----- docs/components/emummc.md | 2 +- docs/components/exosphere.md | 70 ++++++++++++++++++++++++++--- docs/components/fusee.md | 28 ++++++------ docs/components/libraries.md | 2 +- docs/components/mesosphere.md | 4 +- docs/components/modules/ams_mitm.md | 12 ++--- docs/components/modules/loader.md | 24 ++++++---- docs/components/modules/ncm.md | 2 +- docs/components/modules/pgl.md | 4 +- docs/components/modules/ro.md | 4 +- docs/components/sept.md | 14 +++--- docs/components/stratosphere.md | 6 +-- docs/components/thermosphere.md | 4 +- docs/components/troposphere.md | 4 +- docs/features/cheats.md | 2 +- docs/features/configurations.md | 30 +++++++++---- docs/main.md | 16 +++---- 18 files changed, 172 insertions(+), 82 deletions(-) diff --git a/docs/building.md b/docs/building.md index 5e5449c29..d8c920d6e 100644 --- a/docs/building.md +++ b/docs/building.md @@ -4,18 +4,24 @@ Building Atmosphère is a very straightforward process that relies almost exclus ## Dependencies + [devkitA64](https://devkitpro.org) + [devkitARM](https://devkitpro.org) -+ [Python 2 or 3](https://www.python.org) -+ [PyCryptodome](https://pypi.org/project/pycryptodome) ++ [Python 2 or 3](https://www.python.org) (optional) ++ [PyCryptodome](https://pypi.org/project/pycryptodome) (optional) ## Instructions -Follow the guide located [here](https://switchbrew.org/wiki/Setting_up_Development_Environment) to install and configure all the tools necessary for the build process. +1. Follow the guide located [here](https://devkitpro.org/wiki/Getting_Started) to install and configure all the tools necessary for the build process. -Install the following packages via (dkp-)pacman: -+ switch-dev -+ switch-libjpeg-turbo -+ devkitARM -+ devkitarm-rules +2. Install the following packages via (dkp-)pacman: ++ `switch-dev` ++ `switch-libjpeg-turbo` ++ `devkitARM` ++ `devkitarm-rules` -In order to build [Sept](components/sept.md) the pycryptodome PyPi package is required. You may install this package by running `pip install pycryptodome` under the installed Python environment of your choice. You may also want to install the zip package to support the `make dist` recipe. +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. -Finally, clone the Atmosphère repository and run `make` under its root directory. +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. diff --git a/docs/components/emummc.md b/docs/components/emummc.md index f7d45f462..6ae69b9ce 100644 --- a/docs/components/emummc.md +++ b/docs/components/emummc.md @@ -1,4 +1,4 @@ # emummc -emummc is a collaboration project that provides eMMC storage emulation. +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. diff --git a/docs/components/exosphere.md b/docs/components/exosphere.md index 41eee173b..1b7f963da 100644 --- a/docs/components/exosphere.md +++ b/docs/components/exosphere.md @@ -1,12 +1,70 @@ -# Exosphère -Exosphère is a customized reimplementation of the Horizon OS's Secure Monitor. +# 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 expands the original Secure Monitor design by providing custom SMCs (Secure Monitor Calls) necessary to the homebrew ecosystem. Currently, these are: -+ smc_ams_iram_copy -+ smc_ams_write_address -+ smc_ams_get_emummc_config +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. diff --git a/docs/components/fusee.md b/docs/components/fusee.md index 9a47bedd4..4cd777f40 100644 --- a/docs/components/fusee.md +++ b/docs/components/fusee.md @@ -1,22 +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 +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 is also capable of chainloading other payloads (e.g.: Android). -Fusée's behavior can be configured via the [BCT.ini](../features/BCT.md) file located on the SD card. +fusée's behavior can be configured via the [BCT.ini](../features/BCT.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. +## 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. +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-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. +## 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. +This includes setting up the cryptosystem, mounting or emulating the eMMC, injecting or patching system modules and launching the exosphère component. diff --git a/docs/components/libraries.md b/docs/components/libraries.md index 06b13d23d..c3840a681 100644 --- a/docs/components/libraries.md +++ b/docs/components/libraries.md @@ -1,4 +1,4 @@ -# Libraries +# libraries This is a collection of libraries for doing operating system development for the Nintendo Switch. ## libmesosphere diff --git a/docs/components/mesosphere.md b/docs/components/mesosphere.md index 5ee758fa1..a56af4ba8 100644 --- a/docs/components/mesosphere.md +++ b/docs/components/mesosphere.md @@ -1,3 +1,3 @@ -# Mesosphère -Mesosphère is a work in progress customized kernel reimplementation. +# 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. diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index e95dfd1a5..0f17c5d26 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -5,28 +5,30 @@ This module provides methods to intercept services provided by other system modu 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) -+ Homebrew Loader (to allow homebrew to take advantage of the feature) ++ [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: -+ Homebrew Loader (to help homebrew not need to be recompiled due to a breaking change introduced in the past) ++ [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 hbl web browser launching) ++ 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 settings requests ++ 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 #.#.#)`. This allows users to easily verify what version of Atmosphère they are running. +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. diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md index 97204bee4..6ddd3bbd9 100644 --- a/docs/components/modules/loader.md +++ b/docs/components/modules/loader.md @@ -5,6 +5,15 @@ This module is a reimplementation of the Horizon OS's `ldr` system module, which 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 @@ -26,19 +35,16 @@ Atmosphère extends this functionality by also searching for these files on the 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. -It is also 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 -``` - ### 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. 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. + +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. @@ -46,8 +52,10 @@ Because NSO files are compressed, patch files are not made between the original 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 provides first class support for [hbmenu](https://github.com/switchbrew/nx-hbmenu/releases) and [hbloader](https://github.com/switchbrew/nx-hbloader/releases). +### 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. diff --git a/docs/components/modules/ncm.md b/docs/components/modules/ncm.md index 5a4971d91..89bfab956 100644 --- a/docs/components/modules/ncm.md +++ b/docs/components/modules/ncm.md @@ -1,4 +1,4 @@ # ncm -This module is a reimplementation of the Horizon OS's `ncm` system module, which is responsible content management. +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. diff --git a/docs/components/modules/pgl.md b/docs/components/modules/pgl.md index fa1658a8d..305594b22 100644 --- a/docs/components/modules/pgl.md +++ b/docs/components/modules/pgl.md @@ -1,2 +1,4 @@ # pgl -This module is a reimplementation of the Horizon OS's `pgl` system module, which is responsible for launching programs. +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`. diff --git a/docs/components/modules/ro.md b/docs/components/modules/ro.md index 9f6f7d767..25d967aaf 100644 --- a/docs/components/modules/ro.md +++ b/docs/components/modules/ro.md @@ -1,5 +1,7 @@ # ro -This module is a reimplementation of the Horizon OS's `ro` system module, which is responsible for loading dynamic libraries. +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. diff --git a/docs/components/sept.md b/docs/components/sept.md index b080cba7c..9b91969f2 100644 --- a/docs/components/sept.md +++ b/docs/components/sept.md @@ -1,14 +1,14 @@ -# Sept +# 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`. +## 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. +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`. +## 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. +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. diff --git a/docs/components/stratosphere.md b/docs/components/stratosphere.md index e539b887d..8eafc1412 100644 --- a/docs/components/stratosphere.md +++ b/docs/components/stratosphere.md @@ -1,8 +1,8 @@ -# 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. +# 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. ## Modules -The modules currently provided by Stratosphère are: +The modules currently provided by stratosphère are: + [ams_mitm](modules/ams_mitm.md) + [boot](modules/boot.md) + [boot2](modules/boot2.md) diff --git a/docs/components/thermosphere.md b/docs/components/thermosphere.md index c12f7c85d..ad75fb256 100644 --- a/docs/components/thermosphere.md +++ b/docs/components/thermosphere.md @@ -1,3 +1,3 @@ -# Thermosphère -Thermosphère is a work in progress hypervisor implementation. +# 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. diff --git a/docs/components/troposphere.md b/docs/components/troposphere.md index 538bc8d88..91b62afc2 100644 --- a/docs/components/troposphere.md +++ b/docs/components/troposphere.md @@ -1,5 +1,5 @@ -# Troposphère -Troposphère provides customization of the Horizon OS at the application level. +# 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. diff --git a/docs/features/cheats.md b/docs/features/cheats.md index 007a50584..16514bb32 100644 --- a/docs/features/cheats.md +++ b/docs/features/cheats.md @@ -19,7 +19,7 @@ This behavior ensures that cheat codes are only loaded when the user would want 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. diff --git a/docs/features/configurations.md b/docs/features/configurations.md index b296de6fe..858ec443c 100644 --- a/docs/features/configurations.md +++ b/docs/features/configurations.md @@ -2,23 +2,26 @@ 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 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 ``` -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 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. +"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 ``` ``` @@ -29,10 +32,13 @@ nogc = X ### 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] @@ -47,17 +53,23 @@ log_level = X 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. +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 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 ``` @@ -105,15 +117,15 @@ To invert the behavior of the override key, place an exclamation point in front 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 -Settings are parsed from the `/atmosphere/config/system_settings.ini` file during the boot process. This file is a normal ini file, with some specific interpretations. +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 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: +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 = ... ``` -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. +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] diff --git a/docs/main.md b/docs/main.md index c6bd497e3..44e64a7de 100644 --- a/docs/main.md +++ b/docs/main.md @@ -3,17 +3,17 @@ Atmosphère is a work-in-progress customized firmware for the Nintendo Switch. I ## 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) ++ [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) Additionally, Atmosphère also provides the following secondary components: + [emummc](components/emummc.md) -+ [Sept](components/sept.md) -+ [Libraries](components/libraries.md) ++ [sept](components/sept.md) ++ [libraries](components/libraries.md) ## Features Atmosphère provides several original features which add or expand functionalities for the customized firmware environment: From 67b91cfa13a5f74eebe9a454294b73e63a9fbf98 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Mon, 27 Apr 2020 18:52:53 +0100 Subject: [PATCH 012/118] docs: minor style fix --- docs/building.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/building.md b/docs/building.md index d8c920d6e..a96173c9a 100644 --- a/docs/building.md +++ b/docs/building.md @@ -11,17 +11,17 @@ Building Atmosphère is a very straightforward process that relies almost exclus 1. Follow the guide located [here](https://devkitpro.org/wiki/Getting_Started) to install and configure all the tools necessary for the build process. 2. Install the following packages via (dkp-)pacman: -+ `switch-dev` -+ `switch-libjpeg-turbo` -+ `devkitARM` -+ `devkitarm-rules` + + `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) + + `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. From 491ba8fdcfd39a503bedd21b282991fc19aec7d4 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 28 Apr 2020 00:10:30 -0700 Subject: [PATCH 013/118] emummc: fix console reinitialize on 10.x 10.x FS now receives a transfer memory to wipe BIS with and maps it. This requires SVCs that emummc did not give itself access to. This commit adds them, which prevents a FS process abort on re-init. --- emummc/emummc.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/emummc/emummc.json b/emummc/emummc.json index 120d53929..6cb2d5c90 100644 --- a/emummc/emummc.json +++ b/emummc/emummc.json @@ -117,6 +117,8 @@ "svcReplyAndReceiveWithUserBuffer": "0x44", "svcCreateEvent": "0x45", "svcReadWriteRegister": "0x4E", + "svcMapTransferMemory": "0x51", + "svcUnmapTransferMemory": "0x52", "svcCreateInterruptEvent": "0x53", "svcQueryIoMapping": "0x55", "svcCreateDeviceAddressSpace": "0x56", From 72f1e85aba5f7af9ee28bea945052860abf5ec93 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 28 Apr 2020 00:14:42 -0700 Subject: [PATCH 014/118] git subrepo push emummc subrepo: subdir: "emummc" merged: "292a8ad4" upstream: origin: "https://github.com/m4xw/emuMMC" branch: "develop" commit: "292a8ad4" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- emummc/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emummc/.gitrepo b/emummc/.gitrepo index f2d3e1807..df68f68fb 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emuMMC branch = develop - commit = b168ddf5fbb31013ff529a4859110c82b11eb361 - parent = c07f54f3709a4710e0aead6c91139fa0893b5e5c + commit = 292a8ad42c8e9f4c9a474b46a5a3190398581131 + parent = 491ba8fdcfd39a503bedd21b282991fc19aec7d4 method = rebase cmdver = 0.4.1 From f670949ca93d9936003879924f3b3589babd7907 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 29 Apr 2020 00:41:51 -0700 Subject: [PATCH 015/118] os: oh geez look at the time --- .../include/stratosphere/os.hpp | 1 + .../stratosphere/os/os_timer_event.hpp | 79 ++++++ .../stratosphere/os/os_timer_event_api.hpp | 41 +++ .../stratosphere/os/os_timer_event_types.hpp | 63 +++++ .../source/os/impl/os_timer_event_helper.cpp | 73 +++++ .../source/os/impl/os_timer_event_helper.hpp | 31 +++ .../os/impl/os_waitable_holder_impl.hpp | 3 + .../os_waitable_holder_of_timer_event.hpp | 67 +++++ .../source/os/os_timer_event.cpp | 263 ++++++++++++++++++ 9 files changed, 621 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_timer_event.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_timer_event_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_timer_event_helper.cpp create mode 100644 libraries/libstratosphere/source/os/impl/os_timer_event_helper.hpp create mode 100644 libraries/libstratosphere/source/os/impl/os_waitable_holder_of_timer_event.hpp create mode 100644 libraries/libstratosphere/source/os/os_timer_event.cpp diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index e67cd32a6..4db896902 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -33,6 +33,7 @@ #include <stratosphere/os/os_event.hpp> #include <stratosphere/os/os_system_event.hpp> #include <stratosphere/os/os_interrupt_event.hpp> +#include <stratosphere/os/os_timer_event.hpp> #include <stratosphere/os/os_thread_local_storage_api.hpp> #include <stratosphere/os/os_thread.hpp> #include <stratosphere/os/os_message_queue.hpp> diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timer_event.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timer_event.hpp new file mode 100644 index 000000000..aff9beaf2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_timer_event.hpp @@ -0,0 +1,79 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours.hpp> +#include <stratosphere/os/os_timer_event_types.hpp> +#include <stratosphere/os/os_timer_event_api.hpp> + +namespace ams::os { + + class TimerEvent { + NON_COPYABLE(TimerEvent); + NON_MOVEABLE(TimerEvent); + private: + TimerEventType event; + public: + explicit TimerEvent(EventClearMode clear_mode) { + InitializeTimerEvent(std::addressof(this->event), clear_mode); + } + + ~TimerEvent() { + FinalizeTimerEvent(std::addressof(this->event)); + } + + void StartOneShot(TimeSpan first_time) { + return StartOneShotTimerEvent(std::addressof(this->event), first_time); + } + + void StartPeriodic(TimeSpan first_time, TimeSpan interval) { + return StartPeriodicTimerEvent(std::addressof(this->event), first_time, interval); + } + + void Stop() { + return StopTimerEvent(std::addressof(this->event)); + } + + void Wait() { + return WaitTimerEvent(std::addressof(this->event)); + } + + bool TryWait() { + return TryWaitTimerEvent(std::addressof(this->event)); + } + + void Signal() { + return SignalTimerEvent(std::addressof(this->event)); + } + + void Clear() { + return ClearTimerEvent(std::addressof(this->event)); + } + + operator TimerEventType &() { + return this->event; + } + + operator const TimerEventType &() const { + return this->event; + } + + TimerEventType *GetBase() { + return std::addressof(this->event); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_api.hpp new file mode 100644 index 000000000..40d58ef51 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_api.hpp @@ -0,0 +1,41 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours.hpp> +#include <stratosphere/os/os_event_common.hpp> + +namespace ams::os { + + struct TimerEventType; + struct WaitableHolderType; + + void InitializeTimerEvent(TimerEventType *event, EventClearMode clear_mode); + void FinalizeTimerEvent(TimerEventType *event); + + void StartOneShotTimerEvent(TimerEventType *event, TimeSpan first_time); + void StartPeriodicTimerEvent(TimerEventType *event, TimeSpan first_time, TimeSpan interval); + void StopTimerEvent(TimerEventType *event); + + void WaitTimerEvent(TimerEventType *event); + bool TryWaitTimerEvent(TimerEventType *event); + + void SignalTimerEvent(TimerEventType *event); + void ClearTimerEvent(TimerEventType *event); + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, TimerEventType *event); + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp new file mode 100644 index 000000000..44e465429 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_timer_event_types.hpp @@ -0,0 +1,63 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours.hpp> +#include <stratosphere/os/os_event_common.hpp> +#include <stratosphere/os/impl/os_internal_critical_section.hpp> +#include <stratosphere/os/impl/os_internal_condition_variable.hpp> + +namespace ams::os { + + namespace impl { + + class WaitableObjectList; + + } + + struct TimerEventType { + using TimeSpanStorage = TYPED_STORAGE(TimeSpan); + + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + + enum TimerState { + TimerState_Stop = 0, + TimerState_OneShot = 1, + TimerState_Periodic = 2, + }; + + util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage; + + u8 state; + u8 clear_mode; + bool signaled; + u8 timer_state; + u32 broadcast_counter_low; + u32 broadcast_counter_high; + + TimeSpanStorage next_time_to_wakeup; + TimeSpanStorage first; + TimeSpanStorage interval; + + impl::InternalCriticalSectionStorage cs_timer_event; + impl::InternalConditionVariableStorage cv_signaled; + }; + static_assert(std::is_trivial<TimerEventType>::value); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timer_event_helper.cpp b/libraries/libstratosphere/source/os/impl/os_timer_event_helper.cpp new file mode 100644 index 000000000..68e3e42bc --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timer_event_helper.cpp @@ -0,0 +1,73 @@ +/* + * 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 <stratosphere.hpp> +#include "os_timer_event_helper.hpp" + +namespace ams::os::impl { + + TimeSpan SaturatedAdd(TimeSpan t1, TimeSpan t2) { + AMS_ASSERT(t1 >= 0); + AMS_ASSERT(t2 >= 0); + + const u64 u1 = t1.GetNanoSeconds(); + const u64 u2 = t2.GetNanoSeconds(); + + const u64 sum = u1 + u2; + return TimeSpan::FromNanoSeconds(std::min<u64>(std::numeric_limits<s64>::max(), sum)); + } + + void StopTimerUnsafe(TimerEventType *event) { + GetReference(event->next_time_to_wakeup) = 0; + event->timer_state = TimerEventType::TimerState_Stop; + } + + bool UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time) { + TimeSpan next_time = GetReference(event->next_time_to_wakeup); + + switch (event->timer_state) { + case TimerEventType::TimerState_Stop: + break; + case TimerEventType::TimerState_OneShot: + if (next_time < cur_time) { + event->signaled = true; + StopTimerUnsafe(event); + return true; + } + break; + case TimerEventType::TimerState_Periodic: + if (next_time < cur_time) { + event->signaled = true; + + next_time = SaturatedAdd(next_time, GetReference(event->interval)); + if (next_time < cur_time) { + const u64 elapsed_from_first = (cur_time - GetReference(event->first)).GetNanoSeconds(); + const u64 interval = GetReference(event->interval).GetNanoSeconds(); + + const u64 t = std::min<u64>(std::numeric_limits<s64>::max(), ((elapsed_from_first / interval) + 1) * interval); + next_time = SaturatedAdd(TimeSpan::FromNanoSeconds(t), GetReference(event->first)); + } + + GetReference(event->next_time_to_wakeup) = next_time; + return true; + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return false; + } + +} diff --git a/libraries/libstratosphere/source/os/impl/os_timer_event_helper.hpp b/libraries/libstratosphere/source/os/impl/os_timer_event_helper.hpp new file mode 100644 index 000000000..e23c5fae4 --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_timer_event_helper.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> + +namespace ams::os { + + struct TimerEventType; + + namespace impl { + + TimeSpan SaturatedAdd(TimeSpan t1, TimeSpan t2); + void StopTimerUnsafe(TimerEventType *event); + bool UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(TimerEventType *event, TimeSpan cur_time); + + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp index 25b3c02a7..90a3e4631 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp @@ -18,6 +18,7 @@ #include "os_waitable_holder_of_event.hpp" #include "os_waitable_holder_of_inter_process_event.hpp" #include "os_waitable_holder_of_interrupt_event.hpp" +#include "os_waitable_holder_of_timer_event.hpp" #include "os_waitable_holder_of_thread.hpp" #include "os_waitable_holder_of_semaphore.hpp" #include "os_waitable_holder_of_message_queue.hpp" @@ -30,6 +31,7 @@ namespace ams::os::impl { TYPED_STORAGE(WaitableHolderOfEvent) holder_of_event_storage; TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage; TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage; + TYPED_STORAGE(WaitableHolderOfTimerEvent) holder_of_timer_event_storage; TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage; TYPED_STORAGE(WaitableHolderOfSemaphore) holder_of_semaphore_storage; TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage; @@ -44,6 +46,7 @@ namespace ams::os::impl { CHECK_HOLDER(WaitableHolderOfEvent); CHECK_HOLDER(WaitableHolderOfInterProcessEvent); CHECK_HOLDER(WaitableHolderOfInterruptEvent); + CHECK_HOLDER(WaitableHolderOfTimerEvent); CHECK_HOLDER(WaitableHolderOfThread); CHECK_HOLDER(WaitableHolderOfSemaphore); CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull); diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_timer_event.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_timer_event.hpp new file mode 100644 index 000000000..949df7b7c --- /dev/null +++ b/libraries/libstratosphere/source/os/impl/os_waitable_holder_of_timer_event.hpp @@ -0,0 +1,67 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> +#include "os_timer_event_helper.hpp" +#include "os_waitable_holder_base.hpp" +#include "os_waitable_object_list.hpp" + +namespace ams::os::impl { + + class WaitableHolderOfTimerEvent : public WaitableHolderOfUserObject { + private: + TimerEventType *event; + private: + TriBool IsSignaledImpl() const { + TimeSpan cur_time = this->GetManager()->GetCurrentTime(); + UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(this->event, cur_time); + return this->event->signaled ? TriBool::True : TriBool::False; + } + public: + explicit WaitableHolderOfTimerEvent(TimerEventType *e) : event(e) { /* ... */ } + + /* IsSignaled, Link, Unlink implemented. */ + virtual TriBool IsSignaled() const override { + std::scoped_lock lk(GetReference(this->event->cs_timer_event)); + return this->IsSignaledImpl(); + } + + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(GetReference(this->event->cs_timer_event)); + + GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this); + return this->IsSignaledImpl(); + } + + virtual void UnlinkFromObjectList() override { + std::scoped_lock lk(GetReference(this->event->cs_timer_event)); + + GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this); + } + + /* Gets the amount of time remaining until this wakes up. */ + virtual TimeSpan GetAbsoluteWakeupTime() const override { + std::scoped_lock lk(GetReference(this->event->cs_timer_event)); + + if (this->event->timer_state == TimerEventType::TimerState_Stop) { + return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()); + } + + return GetReference(this->event->next_time_to_wakeup); + } + }; + +} diff --git a/libraries/libstratosphere/source/os/os_timer_event.cpp b/libraries/libstratosphere/source/os/os_timer_event.cpp new file mode 100644 index 000000000..ec1b120e4 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_timer_event.cpp @@ -0,0 +1,263 @@ +/* + * 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 <stratosphere.hpp> +#include "impl/os_timer_event_helper.hpp" +#include "impl/os_tick_manager.hpp" +#include "impl/os_timeout_helper.hpp" +#include "impl/os_waitable_object_list.hpp" +#include "impl/os_waitable_holder_impl.hpp" + +namespace ams::os { + + namespace { + + ALWAYS_INLINE u64 GetBroadcastCounterUnsafe(TimerEventType *event) { + const u64 upper = event->broadcast_counter_high; + return (upper << BITSIZEOF(event->broadcast_counter_low)) | event->broadcast_counter_low; + } + + ALWAYS_INLINE void IncrementBroadcastCounterUnsafe(TimerEventType *event) { + if ((++event->broadcast_counter_low) == 0) { + ++event->broadcast_counter_high; + } + } + + ALWAYS_INLINE void SignalTimerEventImplUnsafe(TimerEventType *event) { + /* Set signaled. */ + event->signaled = true; + + /* Signal! */ + if (event->clear_mode == EventClearMode_ManualClear) { + /* If we're manual clear, increment counter and wake all. */ + IncrementBroadcastCounterUnsafe(event); + } else { + /* If we're auto clear, signal one thread, which will clear. */ + GetReference(event->cv_signaled).Signal(); + } + + /* Wake up whatever manager, if any. */ + GetReference(event->waitable_object_list_storage).SignalAllThreads(); + } + + } + + void InitializeTimerEvent(TimerEventType *event, EventClearMode clear_mode) { + /* Initialize internal variables. */ + new (GetPointer(event->cs_timer_event)) impl::InternalCriticalSection; + new (GetPointer(event->cv_signaled)) impl::InternalConditionVariable; + + /* Initialize the waitable object list. */ + new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList(); + + /* Initialize member variables. */ + event->clear_mode = static_cast<u8>(clear_mode); + event->signaled = false; + event->timer_state = TimerEventType::TimerState_Stop; + event->broadcast_counter_low = 0; + event->broadcast_counter_high = 0; + + GetReference(event->next_time_to_wakeup) = 0; + GetReference(event->first) = 0; + GetReference(event->interval) = 0; + + /* Mark initialized. */ + event->state = TimerEventType::State_Initialized; + } + + void FinalizeTimerEvent(TimerEventType *event) { + + /* Mark uninitialized. */ + event->state = TimerEventType::State_NotInitialized; + + /* Destroy objects. */ + GetReference(event->waitable_object_list_storage).~WaitableObjectList(); + GetReference(event->cv_signaled).~InternalConditionVariable(); + GetReference(event->cs_timer_event).~InternalCriticalSection(); + } + + void StartOneShotTimerEvent(TimerEventType *event, TimeSpan first_time) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + AMS_ASSERT(first_time >= 0); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* Get the current time. */ + TimeSpan cur_time = impl::GetCurrentTick().ToTimeSpan(); + + /* Set tracking timespans. */ + GetReference(event->next_time_to_wakeup) = impl::SaturatedAdd(cur_time, first_time); + GetReference(event->first) = first_time; + GetReference(event->interval) = 0; + + /* Set state to OneShot. */ + event->timer_state = TimerEventType::TimerState_OneShot; + + /* Signal. */ + GetReference(event->cv_signaled).Broadcast(); + + /* Wake up whatever manager, if any. */ + GetReference(event->waitable_object_list_storage).SignalAllThreads(); + } + } + + void StartPeriodicTimerEvent(TimerEventType *event, TimeSpan first_time, TimeSpan interval) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + AMS_ASSERT(first_time >= 0); + AMS_ASSERT(interval > 0); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* Get the current time. */ + TimeSpan cur_time = impl::GetCurrentTick().ToTimeSpan(); + + /* Set tracking timespans. */ + GetReference(event->next_time_to_wakeup) = impl::SaturatedAdd(cur_time, first_time); + GetReference(event->first) = first_time; + GetReference(event->interval) = interval; + + /* Set state to Periodic. */ + event->timer_state = TimerEventType::TimerState_Periodic; + + /* Signal. */ + GetReference(event->cv_signaled).Broadcast(); + + /* Wake up whatever manager, if any. */ + GetReference(event->waitable_object_list_storage).SignalAllThreads(); + } + } + + void StopTimerEvent(TimerEventType *event) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* Stop the event. */ + impl::StopTimerUnsafe(event); + + /* Signal. */ + GetReference(event->cv_signaled).Broadcast(); + + /* Wake up whatever manager, if any. */ + GetReference(event->waitable_object_list_storage).SignalAllThreads(); + } + } + + void WaitTimerEvent(TimerEventType *event) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + const auto cur_counter = GetBroadcastCounterUnsafe(event); + while (!event->signaled) { + if (cur_counter != GetBroadcastCounterUnsafe(event)) { + break; + } + + /* Update. */ + auto cur_time = impl::GetCurrentTick().ToTimeSpan(); + if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) { + SignalTimerEventImplUnsafe(event); + break; + } + + /* Check state. */ + if (event->timer_state == TimerEventType::TimerState_Stop) { + GetReference(event->cv_signaled).Wait(GetPointer(event->cs_timer_event)); + } else { + TimeSpan next_time = GetReference(event->next_time_to_wakeup); + impl::TimeoutHelper helper(next_time - cur_time); + GetReference(event->cv_signaled).TimedWait(GetPointer(event->cs_timer_event), helper); + } + } + + if (event->clear_mode == EventClearMode_AutoClear) { + event->signaled = false; + } + } + } + + bool TryWaitTimerEvent(TimerEventType *event) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* Update. */ + auto cur_time = impl::GetCurrentTick().ToTimeSpan(); + if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) { + SignalTimerEventImplUnsafe(event); + } + + bool prev = event->signaled; + + if (event->clear_mode == EventClearMode_AutoClear) { + event->signaled = false; + } + + return prev; + } + } + + void SignalTimerEvent(TimerEventType *event) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* If we're signaled, nothing to do. */ + if (event->signaled) { + return; + } + + /* Update. */ + auto cur_time = impl::GetCurrentTick().ToTimeSpan(); + impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time); + + /* Signal. */ + SignalTimerEventImplUnsafe(event); + } + } + + void ClearTimerEvent(TimerEventType *event) { + AMS_ASSERT(event->state == TimerEventType::State_Initialized); + + { + std::scoped_lock lk(GetReference(event->cs_timer_event)); + + /* Update. */ + auto cur_time = impl::GetCurrentTick().ToTimeSpan(); + if (impl::UpdateSignalStateAndRecalculateNextTimeToWakeupUnsafe(event, cur_time)) { + SignalTimerEventImplUnsafe(event); + } + + /* Clear. */ + event->signaled = false; + } + } + + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, TimerEventType *event) { + AMS_ASSERT(event->state == EventType::State_Initialized); + + new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfTimerEvent(event); + + waitable_holder->user_data = 0; + } + +} From 4d78b834cb416f5c0797101822d6ff4c65d35a16 Mon Sep 17 00:00:00 2001 From: SciresM <Sciresm@gmail.com> Date: Wed, 29 Apr 2020 11:45:58 -0700 Subject: [PATCH 016/118] ldr: add technical semantics for redirection --- docs/components/modules/loader.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md index 6ddd3bbd9..06db345e4 100644 --- a/docs/components/modules/loader.md +++ b/docs/components/modules/loader.md @@ -38,6 +38,21 @@ This allows the replacement of applets, system modules, or even games with homeb ##### 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. +* 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. ``` @@ -53,7 +68,7 @@ Because NSO files are compressed, patch files are not made between the original 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). +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. From e819f3dbcedfdf7e462cc398dd6f66006cd07d09 Mon Sep 17 00:00:00 2001 From: SciresM <Sciresm@gmail.com> Date: Wed, 29 Apr 2020 14:13:34 -0700 Subject: [PATCH 017/118] exefs.nsp --- docs/components/modules/loader.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/components/modules/loader.md b/docs/components/modules/loader.md index 06db345e4..7d3800d89 100644 --- a/docs/components/modules/loader.md +++ b/docs/components/modules/loader.md @@ -46,6 +46,7 @@ loader's semantics for content override can (as you may observe from reading the * 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: From 3bc7c52ade9e4109582554e53b2369eab937d1b0 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 1 May 2020 05:23:37 -0700 Subject: [PATCH 018/118] pm: correct extra application thread allocation counts --- stratosphere/pm/source/impl/pm_resource_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index 3b5b2cc8b..aeedc11ab 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -219,8 +219,8 @@ namespace ams::pm::resource { R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(&total_threads_available, GetResourceLimitHandle(ResourceLimitGroup_System), svc::LimitableResource_ThreadCountMax)); /* See how many threads we're expecting. */ - const s64 total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] - - g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] - + const s64 total_threads_allocated = g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_ThreadCountMax] + + g_resource_limits[ResourceLimitGroup_Application][svc::LimitableResource_ThreadCountMax] + g_resource_limits[ResourceLimitGroup_Applet][svc::LimitableResource_ThreadCountMax]; /* Ensure we don't over-commit threads. */ From 9ddc6e596b4efad204a52a12728a3dcc8952e3d9 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 2 May 2020 22:30:23 -0700 Subject: [PATCH 019/118] kern: fix link error --- libraries/libmesosphere/source/libc/kern_cxx.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/libmesosphere/source/libc/kern_cxx.cpp b/libraries/libmesosphere/source/libc/kern_cxx.cpp index 9262db6f8..c73033cd1 100644 --- a/libraries/libmesosphere/source/libc/kern_cxx.cpp +++ b/libraries/libmesosphere/source/libc/kern_cxx.cpp @@ -13,13 +13,14 @@ * 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 <mesosphere.hpp> #ifdef __cplusplus extern "C" { #endif /* cxx implementation details to be stubbed here, as needed. */ +void __cxa_pure_virtual() { MESOSPHERE_PANIC("pure virtual function call"); } #ifdef __cplusplus } /* extern "C" */ From 3e6031b8f4710ababfab42d77dc954413e32a987 Mon Sep 17 00:00:00 2001 From: hexkyz <mike.hexkyz@gmail.com> Date: Sun, 3 May 2020 15:49:23 +0100 Subject: [PATCH 020/118] docs: fix wrong link (thanks @jul2003) --- docs/components/fusee.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components/fusee.md b/docs/components/fusee.md index 4cd777f40..85682d08d 100644 --- a/docs/components/fusee.md +++ b/docs/components/fusee.md @@ -4,7 +4,7 @@ It is divided into three sub-components: fusée-primary, fusée-mtc and fusée-s fusée is also capable of chainloading other payloads (e.g.: Android). -fusée's behavior can be configured via the [BCT.ini](../features/BCT.md) file located on the SD card. +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. From 85cd2c97a0a3fe824ccf9247d51bb8d4077b06e1 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 4 May 2020 23:29:44 -0700 Subject: [PATCH 021/118] ams:bpc: allow programatically setting reboot payload --- libraries/libstratosphere/source/ams/ams_bpc.c | 2 +- libraries/libstratosphere/source/ams/ams_bpc.h | 2 +- .../libstratosphere/source/ams/ams_environment.cpp | 2 +- .../ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp | 5 ++--- .../ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp | 2 +- .../ams_mitm/source/bpc_mitm/bpc_ams_service.cpp | 10 ++++++---- .../ams_mitm/source/bpc_mitm/bpc_ams_service.hpp | 8 ++++---- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/libraries/libstratosphere/source/ams/ams_bpc.c b/libraries/libstratosphere/source/ams/ams_bpc.c index 8a3ddee4b..1a749dc02 100644 --- a/libraries/libstratosphere/source/ams/ams_bpc.c +++ b/libraries/libstratosphere/source/ams/ams_bpc.c @@ -51,7 +51,7 @@ Result amsBpcRebootToFatalError(void *ctx) { } -Result amsBpcSetInitialPayload(const void *src, size_t src_size) { +Result amsBpcSetRebootPayload(const void *src, size_t src_size) { return serviceDispatch(&g_amsBpcSrv, 65001, .buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias }, .buffers = { { src, src_size } }, diff --git a/libraries/libstratosphere/source/ams/ams_bpc.h b/libraries/libstratosphere/source/ams/ams_bpc.h index d8a3a9ed2..93da63092 100644 --- a/libraries/libstratosphere/source/ams/ams_bpc.h +++ b/libraries/libstratosphere/source/ams/ams_bpc.h @@ -28,7 +28,7 @@ void amsBpcExit(void); Service *amsBpcGetServiceSession(void); Result amsBpcRebootToFatalError(void *ctx); -Result amsBpcSetInitialPayload(const void *src, size_t src_size); +Result amsBpcSetRebootPayload(const void *src, size_t src_size); #ifdef __cplusplus } diff --git a/libraries/libstratosphere/source/ams/ams_environment.cpp b/libraries/libstratosphere/source/ams/ams_environment.cpp index f4bc04199..47babfb92 100644 --- a/libraries/libstratosphere/source/ams/ams_environment.cpp +++ b/libraries/libstratosphere/source/ams/ams_environment.cpp @@ -41,7 +41,7 @@ namespace ams { } void SetInitialRebootPayload(const void *src, size_t src_size) { - R_ABORT_UNLESS(amsBpcSetInitialPayload(src, src_size)); + R_ABORT_UNLESS(amsBpcSetRebootPayload(src, src_size)); } void WEAK_SYMBOL ExceptionHandler(FatalErrorContext *ctx) { diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index 5ca66b265..cc15f63f8 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -97,7 +97,7 @@ namespace ams::mitm::bpc { DoRebootToPayload(ctx); } - void SetInitialRebootPayload(const void *payload, size_t payload_size) { + void SetRebootPayload(const void *payload, size_t payload_size) { /* Clear payload buffer */ std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload)); @@ -107,9 +107,8 @@ namespace ams::mitm::bpc { /* Copy in payload. */ std::memcpy(g_reboot_payload, payload, payload_size); - /* NOTE: Preferred reboot type will be parsed from settings later on. */ + /* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */ g_reboot_type = RebootType::ToPayload; - } Result LoadRebootPayload() { diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp index 0a2ae78bb..b75ae3e39 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp @@ -24,7 +24,7 @@ namespace ams::mitm::bpc { void ShutdownSystem(); /* Atmosphere power utilities. */ - void SetInitialRebootPayload(const void *payload, size_t payload_size); + void SetRebootPayload(const void *payload, size_t payload_size); Result LoadRebootPayload(); Result DetectPreferredRebootFunctionality(); void RebootForFatalError(const ams::FatalErrorContext *ctx); diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp index bd4f63313..79d6c27e1 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp @@ -29,13 +29,15 @@ namespace ams::mitm::bpc { bpc::RebootForFatalError(&ctx); } - void AtmosphereService::SetInitialRebootPayload(const ams::sf::InBuffer &payload) { + void AtmosphereService::SetRebootPayload(const ams::sf::InBuffer &payload) { + /* Set the reboot payload. */ + bpc::SetRebootPayload(payload.GetPointer(), payload.GetSize()); + + /* If this is being called for the first time (by boot sysmodule), */ + /* Then we should kick off the rest of init. */ if (!g_set_initial_payload) { g_set_initial_payload = true; - /* Set the initial reboot payload. */ - bpc::SetInitialRebootPayload(payload.GetPointer(), payload.GetSize()); - /* Start the initialization process. */ ::ams::mitm::StartInitialize(); } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp index 794235691..fb15dc540 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp @@ -22,16 +22,16 @@ namespace ams::mitm::bpc { class AtmosphereService final : public sf::IServiceObject { private: enum class CommandId { - RebootToFatalError = 65000, - SetInitialRebootPayload = 65001, + RebootToFatalError = 65000, + SetRebootPayload = 65001, }; private: void RebootToFatalError(const ams::FatalErrorContext &ctx); - void SetInitialRebootPayload(const ams::sf::InBuffer &payload); + void SetRebootPayload(const ams::sf::InBuffer &payload); public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(RebootToFatalError), - MAKE_SERVICE_COMMAND_META(SetInitialRebootPayload), + MAKE_SERVICE_COMMAND_META(SetRebootPayload), }; }; From 8e75a4169d1355bcaf4e36db70acd5a332578497 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 6 May 2020 22:29:07 -0700 Subject: [PATCH 022/118] ams: revamp target firmware --- exosphere/lp0fw/src/lp0.c | 36 ++-- exosphere/lp0fw/src/secmon.c | 6 +- exosphere/src/bootup.c | 26 +-- exosphere/src/coldboot_init.c | 22 +- exosphere/src/configitem.c | 20 +- exosphere/src/cpu_context.c | 10 +- exosphere/src/fuse.c | 59 +++--- exosphere/src/mc.c | 16 +- exosphere/src/package2.c | 123 +++++------- exosphere/src/package2.h | 2 +- exosphere/src/sc7.c | 14 +- exosphere/src/sealedkeys.c | 14 +- exosphere/src/smc_api.c | 57 ++---- exosphere/src/smc_user.c | 59 +++--- exosphere/src/titlekey.c | 2 +- exosphere/src/warmboot_init.c | 6 +- exosphere/src/warmboot_main.c | 12 +- fusee/fusee-mtc/src/fuse.c | 4 +- fusee/fusee-primary/src/fuse.c | 4 +- fusee/fusee-secondary/src/device_partition.h | 5 +- fusee/fusee-secondary/src/fs_dev.c | 22 +- fusee/fusee-secondary/src/fs_dev.h | 5 +- fusee/fusee-secondary/src/fs_utils.c | 31 ++- fusee/fusee-secondary/src/fs_utils.h | 4 +- fusee/fusee-secondary/src/fuse.c | 13 +- fusee/fusee-secondary/src/fuse.h | 1 + fusee/fusee-secondary/src/key_derivation.c | 78 ++++---- fusee/fusee-secondary/src/lib/log.c | 2 +- fusee/fusee-secondary/src/masterkey.c | 82 ++++++++ fusee/fusee-secondary/src/masterkey.h | 4 + fusee/fusee-secondary/src/nxboot.c | 159 ++++++++++++--- fusee/fusee-secondary/src/nxboot.h | 4 +- fusee/fusee-secondary/src/nxboot_iram.c | 32 +-- fusee/fusee-secondary/src/nxfs.c | 188 +++++++++--------- fusee/fusee-secondary/src/package2.c | 6 +- fusee/fusee-secondary/src/se.c | 83 ++++---- fusee/fusee-secondary/src/se.h | 6 +- .../arm64/init/kern_k_init_page_table.hpp | 2 +- .../include/mesosphere/kern_common.hpp | 18 +- .../include/stratosphere/ams/ams_types.hpp | 28 +-- .../include/stratosphere/hos/hos_types.hpp | 57 ++++-- .../sf/cmif/sf_cmif_service_dispatch.hpp | 2 +- .../source/hos/hos_version_api.cpp | 110 +--------- .../include/vapours/ams/ams_target_firmware.h | 104 ++++++++-- .../source/arch/arm64/init/kern_init_core.cpp | 2 +- .../kernel_ldr/source/kern_init_loader.cpp | 8 +- sept/sept-primary/src/fuse.c | 4 +- sept/sept-secondary/src/fuse.c | 4 +- 48 files changed, 863 insertions(+), 693 deletions(-) diff --git a/exosphere/lp0fw/src/lp0.c b/exosphere/lp0fw/src/lp0.c index 8a6fae081..5bd0b8a99 100644 --- a/exosphere/lp0fw/src/lp0.c +++ b/exosphere/lp0fw/src/lp0.c @@ -13,7 +13,7 @@ * 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 "lp0.h" #include "mc.h" @@ -41,56 +41,56 @@ void lp0_entry_main(warmboot_metadata_t *meta) { if (meta->magic != WARMBOOT_MAGIC || target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX) { reboot(); } - + /* [4.0.0+] First thing warmboot does is disable BPMP access to memory. */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { disable_bpmp_access_to_dram(); } - + /* Configure debugging depending on FUSE_PRODUCTION_MODE */ misc_configure_device_dbg_settings(); - + /* Check for downgrade. */ /* NOTE: We implemented this as "return false" */ if (fuse_check_downgrade_status()) { reboot(); } - - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_300) { + + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { /* Nintendo's firmware checks APBDEV_PMC_SECURE_SCRATCH32_0 against a per-warmboot binary value here. */ /* We won't bother with that. */ if (false /* APBDEV_PMC_SECURE_SCRATCH32_0 == WARMBOOT_MAGIC_NUMBER */) { reboot(); } } - + /* TODO: Check that we're running at the correct physical address. */ - + /* Setup fuses, disable bypass. */ fuse_configure_fuse_bypass(); - + /* Configure oscillators/timing in CAR. */ car_configure_oscillators(); - + /* Restore RAM configuration. */ misc_restore_ram_svop(); emc_configure_pmacro_training(); - + /* Setup clock output for all devices, working around mbist bug. */ car_mbist_workaround(); - + /* Initialize the CPU cluster. */ cluster_initialize_cpu(); - + secmon_restore_to_tzram(target_firmware); - + /* Power on the CPU cluster. */ cluster_power_on_cpu(); - + /* Nintendo clears most of warmboot.bin out of IRAM here. We're not gonna bother. */ /* memset( ... ); */ - - const uint32_t halt_val = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) ? 0x40000000 : 0x50000000; + + const uint32_t halt_val = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? 0x40000000 : 0x50000000; while (true) { /* Halt the BPMP. */ FLOW_CTLR_HALT_COP_EVENTS_0 = halt_val; diff --git a/exosphere/lp0fw/src/secmon.c b/exosphere/lp0fw/src/secmon.c index 24f5154ab..f39c89fa9 100644 --- a/exosphere/lp0fw/src/secmon.c +++ b/exosphere/lp0fw/src/secmon.c @@ -13,7 +13,7 @@ * 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" @@ -30,13 +30,13 @@ static void secmon_decrypt_saved_image(void *dst, const void *src, size_t size); void secmon_restore_to_tzram(const uint32_t target_firmware) { /* Newer warmboot binaries clear the untouched keyslots for safety. */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { secmon_clear_unused_keyslots(); } /* Decrypt Secure Monitor from DRAM into TZRAM. */ void *tzram_src = (void *)(0x80010000); - void *tzram_dst = (void *)(target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_500 ? 0x7C012000 : 0x7C010000); + void *tzram_dst = (void *)(target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 ? 0x7C012000 : 0x7C010000); const size_t tzram_size = 0xE000; secmon_decrypt_saved_image(tzram_dst, tzram_src, tzram_size); diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c index 7dab360d8..47916e315 100644 --- a/exosphere/src/bootup.c +++ b/exosphere/src/bootup.c @@ -52,7 +52,7 @@ void setup_dram_magic_numbers(void) { unsigned int target_fw = exosphere_get_target_firmware(); (*(volatile uint32_t *)(0x8005FFFC)) = 0xC0EDBBCC; /* Access test value. */ flush_dcache_range((void *)0x8005FFFC, (void *)0x80060000); - if (ATMOSPHERE_TARGET_FIRMWARE_600 <= target_fw) { + if (ATMOSPHERE_TARGET_FIRMWARE_6_0_0 <= target_fw) { (*(volatile uint32_t *)(0x8005FF00)) = 0x00000083; /* SKU code. */ (*(volatile uint32_t *)(0x8005FF04)) = 0x00000002; (*(volatile uint32_t *)(0x8005FF08)) = 0x00000210; /* Tegra210 code. */ @@ -81,7 +81,7 @@ void bootup_misc_mmio(void) { se_generate_random_key(KEYSLOT_SWITCH_SRKGENKEY, KEYSLOT_SWITCH_RNGKEY); se_generate_srk(KEYSLOT_SWITCH_SRKGENKEY); - if (!g_has_booted_up && (ATMOSPHERE_TARGET_FIRMWARE_600 > exosphere_get_target_firmware())) { + if (!g_has_booted_up && (ATMOSPHERE_TARGET_FIRMWARE_6_0_0 > exosphere_get_target_firmware())) { setup_dram_magic_numbers(); } @@ -116,7 +116,7 @@ void bootup_misc_mmio(void) { configure_default_carveouts(); /* Mark registers secure world only. */ - if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_100) { + if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 = APB_SSER0_SATA_AUX | APB_SSER0_DTV | APB_SSER0_QSPI | APB_SSER0_SATA | APB_SSER0_LA; APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 = APB_SSER1_SPI1 | APB_SSER1_SPI2 | APB_SSER1_SPI3 | APB_SSER1_SPI5 | APB_SSER1_SPI6 | APB_SSER1_I2C4 | APB_SSER1_I2C6; APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 = 1 << 4 | 1 << 5 | APB_SSER2_DDS; /* bits 4 and 5 are not labeled in 21.1.7.3 */ @@ -133,7 +133,7 @@ void bootup_misc_mmio(void) { /* Also mark I2C4 secure only, */ sec_disable_1 |= APB_SSER1_I2C4; } - if (hardware_type != 0 && exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (hardware_type != 0 && exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { /* Starting on 4.x on non-dev units, mark UARTB, UARTC, SPI4, I2C3 secure only. */ sec_disable_1 |= APB_SSER1_UART_B | APB_SSER1_UART_C | APB_SSER1_SPI4 | APB_SSER1_I2C3; /* Starting on 4.x on non-dev units, mark SDMMC1 secure only. */ @@ -151,7 +151,7 @@ void bootup_misc_mmio(void) { MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_4) = 0xFFFFFFFF; /* Set SMMU ASID security registers. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0xE; } else { MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0x0; @@ -164,7 +164,7 @@ void bootup_misc_mmio(void) { MAKE_MC_REG(MC_SMMU_ASID_SECURITY_6) = 0; MAKE_MC_REG(MC_SMMU_ASID_SECURITY_7) = 0; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { MAKE_MC_REG(MC_SMMU_PTB_ASID) = 0; } MAKE_MC_REG(MC_SMMU_PTB_DATA) = 0; @@ -180,7 +180,7 @@ void bootup_misc_mmio(void) { /* Clear RESET Vector, setup CPU Secure Boot RESET Vectors. */ uint32_t reset_vec; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { reset_vec = TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN); } else { reset_vec = TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN); @@ -206,7 +206,7 @@ void bootup_misc_mmio(void) { intr_set_cpu_mask(INTERRUPT_ID_SECURITY_ENGINE, 8); intr_set_edge_level(INTERRUPT_ID_SECURITY_ENGINE, 0); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { intr_set_priority(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); intr_set_group(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); intr_set_enabled(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 1); @@ -221,14 +221,14 @@ void bootup_misc_mmio(void) { uart_init(UART_A, 115200); intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler); } for (unsigned int core = 1; core < NUM_CPU_CORES; core++) { set_core_is_active(core, false); } g_has_booted_up = true; - } else if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) { + } else if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { /* Disable AHB redirect. */ MAKE_MC_REG(MC_IRAM_BOM) = 0xFFFFF000; MAKE_MC_REG(MC_IRAM_TOM) = 0; @@ -238,7 +238,7 @@ void bootup_misc_mmio(void) { } void setup_4x_mmio(void) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { configure_gpu_ucode_carveout(); } @@ -361,9 +361,9 @@ void identity_unmap_iram_cd_tzram(void) { } void secure_additional_devices(void) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 |= APB_SSER0_PMC; /* make PMC secure-only (2.x+) */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 |= APB_SSER1_MC0 | APB_SSER1_MC1 | APB_SSER1_MCB; /* make MC0, MC1, MCB secure-only (4.x+) */ } } diff --git a/exosphere/src/coldboot_init.c b/exosphere/src/coldboot_init.c index cd138419d..19f0576fb 100644 --- a/exosphere/src/coldboot_init.c +++ b/exosphere/src/coldboot_init.c @@ -13,7 +13,7 @@ * 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 <string.h> #include "utils.h" #include "arm.h" @@ -26,7 +26,7 @@ #undef MAILBOX_NX_BOOTLOADER_BASE #undef TIMERS_BASE -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) ? (MMIO_GET_DEVICE_7X_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) : (MMIO_GET_DEVICE_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) +#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (MMIO_GET_DEVICE_7X_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) : (MMIO_GET_DEVICE_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) #define TIMERS_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_TMRs_WDTs)) extern const uint8_t __start_cold[]; @@ -52,11 +52,11 @@ static void mmio_map_all_devices(uintptr_t *mmu_l3_tbl, unsigned int target_firm static const uintptr_t pas[] = { TUPLE_FOLD_LEFT_0(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; static const bool is_secure[] = { TUPLE_FOLD_LEFT_2(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; - + static const uintptr_t pas_7x[] = { TUPLE_FOLD_LEFT_0(EVAL(MMIO_DEVID_MAX), _MMAPDEV7X, COMMA) }; for(size_t i = 0, offset = 0; i < MMIO_DEVID_MAX; i++) { - uintptr_t pa = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) ? pas[i] : pas_7x[i]; + uintptr_t pa = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? pas[i] : pas_7x[i]; mmio_map_device(mmu_l3_tbl, MMIO_BASE + offset, pa, sizes[i], is_secure[i]); offset += sizes[i]; offset += 0x1000; @@ -92,9 +92,9 @@ static void tzram_map_all_segments(uintptr_t *mmu_l3_tbl, unsigned int target_fi static const bool is_executable[] = { TUPLE_FOLD_LEFT_3(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZS, COMMA) }; static const uintptr_t offs_5x[] = { TUPLE_FOLD_LEFT_0(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZ5XS, COMMA) }; - + for(size_t i = 0, offset = 0; i < TZRAM_SEGMENT_ID_MAX; i++) { - uintptr_t off = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_500) ? offs[i] : offs_5x[i]; + uintptr_t off = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) ? offs[i] : offs_5x[i]; tzram_map_segment(mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, 0x7C010000ull + off, sizes[i], is_executable[i]); offset += increments[i]; } @@ -102,9 +102,9 @@ static void tzram_map_all_segments(uintptr_t *mmu_l3_tbl, unsigned int target_fi static void configure_ttbls(unsigned int target_firmware) { uintptr_t *mmu_l1_tbl; - uintptr_t *mmu_l2_tbl; + uintptr_t *mmu_l2_tbl; uintptr_t *mmu_l3_tbl; - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_500) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); mmu_l2_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); @@ -154,12 +154,12 @@ uintptr_t get_coldboot_crt0_temp_stack_address(void) { } uintptr_t get_coldboot_crt0_stack_address(void) { - if (exosphere_get_target_firmware_for_init() < ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware_for_init() < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { return TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; } else { return TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; } - + } void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, uintptr_t start_cold) { @@ -196,7 +196,7 @@ void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, uintptr_t start_cold) init_dma_controllers(g_exosphere_target_firmware_for_init); configure_ttbls(g_exosphere_target_firmware_for_init); - if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_500) { + if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { set_memory_registers_enable_mmu_1x_ttbr0(); } else { set_memory_registers_enable_mmu_5x_ttbr0(); diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index f538ab8b3..a69de35b0 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -203,7 +203,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_BOOTREASON: /* For some reason, Nintendo removed it on 4.0 */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { *p_outvalue = bootconfig_get_boot_reason(); } else { result = 2; @@ -238,7 +238,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_ISQUESTUNIT: /* Added on 3.0, used to determine whether console is a kiosk unit. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_300) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { *p_outvalue = (fuse_get_reserved_odm(4) >> 10) & 1; } else { result = 2; @@ -246,7 +246,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_NEWHARDWARETYPE_5X: /* Added in 5.x, currently hardcoded to 0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { *p_outvalue = 0; } else { result = 2; @@ -254,7 +254,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_NEWKEYGENERATION_5X: /* Added in 5.x. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { *p_outvalue = fuse_get_5x_key_generation(); } else { result = 2; @@ -262,7 +262,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_PACKAGE2HASH_5X: /* Added in 5.x. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500 && bootconfig_is_recovery_boot()) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 && bootconfig_is_recovery_boot()) { bootconfig_get_package2_hash_for_recovery(p_outvalue); } else { result = 2; @@ -270,11 +270,11 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) break; case CONFIGITEM_EXOSPHERE_VERSION: /* UNOFFICIAL: Gets information about the current exosphere version. */ - *p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 32ull) | - ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 24ull) | - ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 16ull) | - ((uint64_t)(exosphere_get_target_firmware() & 0xFF) << 8ull) | - ((uint64_t)(mkey_get_revision() & 0xFF) << 0ull); + *p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56ull) | + ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48ull) | + ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40ull) | + ((uint64_t)(mkey_get_revision() & 0xFF) << 32ull) | + ((uint64_t)(exosphere_get_target_firmware()) << 0ull); break; case CONFIGITEM_NEEDS_REBOOT: /* UNOFFICIAL: The fact that we are executing means we aren't in the process of rebooting. */ diff --git a/exosphere/src/cpu_context.c b/exosphere/src/cpu_context.c index a532da505..914467194 100644 --- a/exosphere/src/cpu_context.c +++ b/exosphere/src/cpu_context.c @@ -13,7 +13,7 @@ * 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 <stdbool.h> #include <stdint.h> #include "arm.h" @@ -99,8 +99,8 @@ uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { static const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; static const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { /* Reset the core */ CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 = (1 << (core + 0x10)) | (1 << core); } @@ -133,11 +133,11 @@ uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { } CPU_ON_SUCCESS: - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { /* Start the core */ CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 = (1 << (core + 0x10)) | (1 << core); } - + return 0; } diff --git a/exosphere/src/fuse.c b/exosphere/src/fuse.c index 0ea888598..b10b6f7a8 100644 --- a/exosphere/src/fuse.c +++ b/exosphere/src/fuse.c @@ -196,7 +196,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -205,7 +205,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; @@ -262,30 +262,39 @@ uint32_t fuse_get_5x_key_generation(void) { /* Returns the fuse version expected for the firmware. */ uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware) { - static const uint8_t expected_versions[ATMOSPHERE_TARGET_FIRMWARE_COUNT+1] = { - [ATMOSPHERE_TARGET_FIRMWARE_100] = 1, - [ATMOSPHERE_TARGET_FIRMWARE_200] = 2, - [ATMOSPHERE_TARGET_FIRMWARE_300] = 3, - /* [ATMOSPHERE_TARGET_FIRMWARE_302] = 4, */ - [ATMOSPHERE_TARGET_FIRMWARE_400] = 5, - [ATMOSPHERE_TARGET_FIRMWARE_500] = 6, - [ATMOSPHERE_TARGET_FIRMWARE_600] = 7, - [ATMOSPHERE_TARGET_FIRMWARE_620] = 8, - [ATMOSPHERE_TARGET_FIRMWARE_700] = 9, - [ATMOSPHERE_TARGET_FIRMWARE_800] = 9, - [ATMOSPHERE_TARGET_FIRMWARE_810] = 10, - [ATMOSPHERE_TARGET_FIRMWARE_900] = 11, - [ATMOSPHERE_TARGET_FIRMWARE_910] = 12, - [ATMOSPHERE_TARGET_FIRMWARE_1000] = 13, - }; - - if (target_firmware > ATMOSPHERE_TARGET_FIRMWARE_COUNT) { - generic_panic(); + if (fuse_get_retail_type() != 0) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + return 13; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_1_0) { + return 12; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0) { + return 11; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { + return 10; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { + return 9; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { + return 8; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { + return 7; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { + return 6; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + return 5; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_2) { + return 4; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { + return 3; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { + return 2; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { + return 1; + } else { + return 0; + } + } else { + return (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) ? 1 : 0; } - if (fuse_get_retail_type() != 0) - return expected_versions[target_firmware]; - else - return (target_firmware > ATMOSPHERE_TARGET_FIRMWARE_200) ? 1 : 0; } /* Check for RCM bug patches. */ diff --git a/exosphere/src/mc.c b/exosphere/src/mc.c index 1cb794323..41e999124 100644 --- a/exosphere/src/mc.c +++ b/exosphere/src/mc.c @@ -47,7 +47,7 @@ void configure_gpu_ucode_carveout(void) { carveout->size_big_pages = 2; /* 0x40000 */ carveout->client_access_0 = 0; carveout->client_access_1 = 0; - carveout->client_access_2 = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) ? (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR) | BIT(CSR_TSECSRD)) : (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR)); + carveout->client_access_2 = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) ? (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR) | BIT(CSR_TSECSRD)) : (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR)); carveout->client_access_3 = 0; carveout->client_access_4 = (BIT(CSR_GPUSRD2) | BIT(CSW_GPUSWR2)); carveout->client_force_internal_access_0 = 0; @@ -77,7 +77,7 @@ void configure_default_carveouts(void) { carveout->config = 0x4000006; /* Configure Carveout 2 (GPU UCODE) */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { configure_gpu_ucode_carveout(); } @@ -99,7 +99,7 @@ void configure_default_carveouts(void) { carveout->config = 0x4401E7E; /* Configure default Kernel carveouts based on 2.0.0+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { /* Configure Carveout 4 (KERNEL_BUILTINS) */ configure_kernel_carveout(4, g_saved_carveouts[0].address, g_saved_carveouts[0].size); @@ -140,11 +140,11 @@ void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint6 carveout->size_big_pages = (uint32_t)(size >> 17); carveout->client_access_0 = (BIT(CSR_PTCR) | BIT(CSR_DISPLAY0A) | BIT(CSR_DISPLAY0AB) | BIT(CSR_DISPLAY0B) | BIT(CSR_DISPLAY0BB) | BIT(CSR_DISPLAY0C) | BIT(CSR_DISPLAY0CB) | BIT(CSR_AFIR) | BIT(CSR_DISPLAYHC) | BIT(CSR_DISPLAYHCB) | BIT(CSR_HDAR) | BIT(CSR_HOST1XDMAR) | BIT(CSR_HOST1XR) | BIT(CSR_NVENCSRD) | BIT(CSR_PPCSAHBDMAR) | BIT(CSR_PPCSAHBSLVR)); carveout->client_access_1 = (BIT(CSR_MPCORER) | BIT(CSW_NVENCSWR) | BIT(CSW_AFIW) | BIT(CSW_HDAW) | BIT(CSW_HOST1XW) | BIT(CSW_MPCOREW) | BIT(CSW_PPCSAHBDMAW) | BIT(CSW_PPCSAHBSLVW)); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_810) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW)); carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) { + } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0) { carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW)); carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR) | BIT(CSR_TSECSRDB) | BIT(CSW_TSECSWRB)); @@ -153,10 +153,10 @@ void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint6 carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); } - carveout->client_force_internal_access_0 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSR_AVPCARM7R) : 0; - carveout->client_force_internal_access_1 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSW_AVPCARM7W) : 0; + carveout->client_force_internal_access_0 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) && (carveout_id == 4)) ? BIT(CSR_AVPCARM7R) : 0; + carveout->client_force_internal_access_1 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) && (carveout_id == 4)) ? BIT(CSW_AVPCARM7W) : 0; carveout->client_force_internal_access_2 = 0; carveout->client_force_internal_access_3 = 0; carveout->client_force_internal_access_4 = 0; - carveout->config = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) ? 0x4CB : 0x8B; + carveout->config = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0) ? 0x4CB : 0x8B; } diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index fc3070e76..32bb76ad8 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -82,7 +82,7 @@ static void derive_new_device_keys(unsigned int keygen_keyslot) { break; } else if (relative_revision == mkey_get_revision()) { /* On 7.0.0, sept will have derived this key for us already. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_700) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10); } } else { @@ -133,25 +133,15 @@ static void setup_se(void) { mkey_detect_revision(); /* Derive new device keys. */ - switch (exosphere_get_target_firmware()) { - case ATMOSPHERE_TARGET_FIRMWARE_100: - case ATMOSPHERE_TARGET_FIRMWARE_200: - case ATMOSPHERE_TARGET_FIRMWARE_300: - break; - case ATMOSPHERE_TARGET_FIRMWARE_400: - derive_new_device_keys(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY); - break; - case ATMOSPHERE_TARGET_FIRMWARE_500: - case ATMOSPHERE_TARGET_FIRMWARE_600: - case ATMOSPHERE_TARGET_FIRMWARE_620: - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - case ATMOSPHERE_TARGET_FIRMWARE_810: - case ATMOSPHERE_TARGET_FIRMWARE_900: - case ATMOSPHERE_TARGET_FIRMWARE_910: - case ATMOSPHERE_TARGET_FIRMWARE_1000: + { + const uint32_t target_fw = exosphere_get_target_firmware(); + if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY); - break; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + derive_new_device_keys(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY); + } else { + /* No new keys to derive */ + } } se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); @@ -176,7 +166,7 @@ static void setup_boot_config(void) { bootconfig_clear(); } else { void *bootconfig_ptr = NX_BOOTLOADER_BOOTCONFIG_POINTER; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { bootconfig_ptr = NX_BOOTLOADER_BOOTCONFIG_POINTER_6X; } flush_dcache_range((uint8_t *)bootconfig_ptr, (uint8_t *)bootconfig_ptr + sizeof(bootconfig_t)); @@ -447,30 +437,20 @@ static void load_package2_sections(package2_meta_t *metadata, uint32_t master_ke static void copy_warmboot_bin_to_dram() { uint8_t *warmboot_src; - switch (exosphere_get_target_firmware()) { - case ATMOSPHERE_TARGET_FIRMWARE_100: - case ATMOSPHERE_TARGET_FIRMWARE_200: - case ATMOSPHERE_TARGET_FIRMWARE_300: - default: - generic_panic(); - break; - case ATMOSPHERE_TARGET_FIRMWARE_400: - case ATMOSPHERE_TARGET_FIRMWARE_500: - warmboot_src = (uint8_t *)0x4003B000; - break; - case ATMOSPHERE_TARGET_FIRMWARE_600: - case ATMOSPHERE_TARGET_FIRMWARE_620: - warmboot_src = (uint8_t *)0x4003D800; - break; - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - case ATMOSPHERE_TARGET_FIRMWARE_810: - case ATMOSPHERE_TARGET_FIRMWARE_900: - case ATMOSPHERE_TARGET_FIRMWARE_910: - case ATMOSPHERE_TARGET_FIRMWARE_1000: + + { + const uint32_t target_fw = exosphere_get_target_firmware(); + if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { warmboot_src = (uint8_t *)0x4003E000; - break; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { + warmboot_src = (uint8_t *)0x4003D800; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + warmboot_src = (uint8_t *)0x4003B000; + } else { + return; + } } + uint8_t *warmboot_dst = (uint8_t *)0x8000D000; const size_t warmboot_size = 0x2000; @@ -522,40 +502,31 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { setup_se(); /* Perform initial PMC register writes, if relevant. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { MAKE_REG32(PMC_BASE + 0x054) = 0x8000D000; MAKE_REG32(PMC_BASE + 0x0A0) &= 0xFFF3FFFF; MAKE_REG32(PMC_BASE + 0x818) &= 0xFFFFFFFE; MAKE_REG32(PMC_BASE + 0x334) |= 0x10; - switch (exosphere_get_target_firmware()) { - case ATMOSPHERE_TARGET_FIRMWARE_400: - MAKE_REG32(PMC_BASE + 0x360) = 0x105; - break; - case ATMOSPHERE_TARGET_FIRMWARE_500: - MAKE_REG32(PMC_BASE + 0x360) = 6; - break; - case ATMOSPHERE_TARGET_FIRMWARE_600: - MAKE_REG32(PMC_BASE + 0x360) = 0x87; - break; - case ATMOSPHERE_TARGET_FIRMWARE_620: - MAKE_REG32(PMC_BASE + 0x360) = 0xA8; - break; - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - MAKE_REG32(PMC_BASE + 0x360) = 0x129; - break; - case ATMOSPHERE_TARGET_FIRMWARE_810: - MAKE_REG32(PMC_BASE + 0x360) = 0x14A; - break; - case ATMOSPHERE_TARGET_FIRMWARE_900: - MAKE_REG32(PMC_BASE + 0x360) = 0x16B; - break; - case ATMOSPHERE_TARGET_FIRMWARE_910: - MAKE_REG32(PMC_BASE + 0x360) = 0x18C; - break; - case ATMOSPHERE_TARGET_FIRMWARE_1000: - MAKE_REG32(PMC_BASE + 0x360) = 0x1AD; - break; + + const uint32_t target_fw = exosphere_get_target_firmware(); + if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x105; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_9_1_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x18C; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x16B; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x14A; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x129; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x0A8; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x087; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x006; + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + MAKE_REG32(PMC_BASE + 0x360) = 0x105; } } @@ -587,7 +558,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { setup_boot_config(); /* Set sysctr0 registers based on bootconfig. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { uint64_t sysctr0_val = bootconfig_get_value_for_sysctr0(); MAKE_SYSCTR0_REG(0x8) = (uint32_t)((sysctr0_val >> 0) & 0xFFFFFFFFULL); MAKE_SYSCTR0_REG(0xC) = (uint32_t)((sysctr0_val >> 32) & 0xFFFFFFFFULL); @@ -595,10 +566,10 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { } /* Synchronize with NX BOOTLOADER. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X); copy_warmboot_bin_to_dram(); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { setup_dram_magic_numbers(); } sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2_4X); @@ -651,7 +622,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { } /* Synchronize with NX BOOTLOADER. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED_4X); setup_4x_mmio(); } else { diff --git a/exosphere/src/package2.h b/exosphere/src/package2.h index ee2546cef..ab2d9d01e 100644 --- a/exosphere/src/package2.h +++ b/exosphere/src/package2.h @@ -26,7 +26,7 @@ /* Physaddr 0x40002EF8 */ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_NXBOOTLOADER_MAILBOX) + ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_700) ? (0x000ull) : (0xE00ull)); + return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_NXBOOTLOADER_MAILBOX) + ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (0x000ull) : (0xE00ull)); } #define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (get_nx_bootloader_mailbox_base(targetfw)) diff --git a/exosphere/src/sc7.c b/exosphere/src/sc7.c index 48befadc2..12e28a98c 100644 --- a/exosphere/src/sc7.c +++ b/exosphere/src/sc7.c @@ -90,14 +90,14 @@ static void mitigate_jamais_vu(void) { } /* For debugging, make this check always pass. */ - if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400 || (get_debug_authentication_status() & 3) == 3)) { + if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0 || (get_debug_authentication_status() & 3) == 3)) { FLOW_CTLR_HALT_COP_EVENTS_0 = 0x50000000; } else { FLOW_CTLR_HALT_COP_EVENTS_0 = 0x40000000; } /* Jamais Vu mitigation #2: Ensure the BPMP is halted. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400 || (get_debug_authentication_status() & 3) == 3) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0 || (get_debug_authentication_status() & 3) == 3) { /* BPMP should just be plainly halted, in debugging conditions. */ if (FLOW_CTLR_HALT_COP_EVENTS_0 != 0x50000000) { generic_panic(); @@ -167,7 +167,7 @@ static void save_tzram_state(void) { uint8_t *tzram_encryption_dst = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_ENCRYPTED_TZRAM)); uint8_t *tzram_encryption_src = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM)); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { tzram_encryption_src += 0x2000ull; } uint8_t *tzram_store_address = (uint8_t *)(WARMBOOT_GET_RAM_SEGMENT_ADDRESS(WARMBOOT_RAM_SEGMENT_ID_TZRAM)); @@ -204,7 +204,7 @@ static void save_tzram_state(void) { APBDEV_PMC_SEC_DISABLE8_0 = 0x550000; /* Perform pre-2.0.0 PMC writes. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { /* TODO: Give these writes appropriate defines in pmc.h */ /* Save Encrypted context location + lock scratch register. */ @@ -272,7 +272,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen notify_pmic_shutdown(); /* Validate that the shutdown has correct context. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { mitigate_jamais_vu(); } @@ -280,7 +280,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen configure_pmc_for_deep_powerdown(); /* Ensure that BPMP SC7 firmware is active. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { setup_bpmp_sc7_firmware(); } @@ -294,7 +294,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen /* Ensure that other cores are already asleep. */ if (!(APBDEV_PMC_PWRGATE_STATUS_0 & 0xE00)) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_200) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { call_with_stack_pointer(get_smc_core012_stack_address(), save_se_and_power_down_cpu); } else { save_se_and_power_down_cpu(); diff --git a/exosphere/src/sealedkeys.c b/exosphere/src/sealedkeys.c index a2fe80596..dd4e7f835 100644 --- a/exosphere/src/sealedkeys.c +++ b/exosphere/src/sealedkeys.c @@ -13,7 +13,7 @@ * 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 <string.h> @@ -37,7 +37,7 @@ static const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX_5X][0x10] = { }; bool usecase_is_invalid(unsigned int usecase) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { return usecase >= CRYPTOUSECASE_MAX_5X; } else { return usecase >= CRYPTOUSECASE_MAX; @@ -59,7 +59,7 @@ void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size) if (dst_size != 0x10 || src_size != 0x10) { generic_panic(); } - + seal_key_internal(dst, src, g_titlekey_seal_key_source); } @@ -68,7 +68,7 @@ void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { if (src_size != 0x10) { generic_panic(); } - + unseal_key_internal(keyslot, src, g_titlekey_seal_key_source); } @@ -77,8 +77,8 @@ void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsi if (usecase_is_invalid(usecase) || dst_size != 0x10 || src_size != 0x10) { generic_panic(); } - - + + seal_key_internal(dst, src, g_seal_key_sources[usecase]); } @@ -86,6 +86,6 @@ void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned if (usecase_is_invalid(usecase) || src_size != 0x10) { generic_panic(); } - + unseal_key_internal(keyslot, src, g_seal_key_sources[usecase]); } \ No newline at end of file diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c index 2338102ac..4b3673f6c 100644 --- a/exosphere/src/smc_api.c +++ b/exosphere/src/smc_api.c @@ -169,36 +169,23 @@ void set_suspend_for_debug(void) { } void set_version_specific_smcs(void) { - switch (exosphere_get_target_firmware()) { - case ATMOSPHERE_TARGET_FIRMWARE_100: - /* 1.0.0 doesn't have ConfigureCarveout or ReadWriteRegister. */ - g_smc_priv_table[7].handler = NULL; - g_smc_priv_table[8].handler = NULL; - /* 1.0.0 doesn't have UnwrapAesWrappedTitlekey. */ - g_smc_user_table[0x12].handler = NULL; - break; - case ATMOSPHERE_TARGET_FIRMWARE_200: - case ATMOSPHERE_TARGET_FIRMWARE_300: - case ATMOSPHERE_TARGET_FIRMWARE_400: - /* Do nothing. */ - break; - case ATMOSPHERE_TARGET_FIRMWARE_500: - case ATMOSPHERE_TARGET_FIRMWARE_600: - case ATMOSPHERE_TARGET_FIRMWARE_620: - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - case ATMOSPHERE_TARGET_FIRMWARE_810: - case ATMOSPHERE_TARGET_FIRMWARE_900: - case ATMOSPHERE_TARGET_FIRMWARE_910: - case ATMOSPHERE_TARGET_FIRMWARE_1000: - /* No more LoadSecureExpModKey. */ - g_smc_user_table[0xE].handler = NULL; - g_smc_user_table[0xC].id = 0xC300D60C; - g_smc_user_table[0xC].handler = smc_encrypt_rsa_key_for_import; - g_smc_user_table[0xD].handler = smc_decrypt_or_import_rsa_key; - break; - default: - panic_predefined(0xA); + const uint32_t target_firmware = exosphere_get_target_firmware(); + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { + /* No more LoadSecureExpModKey. */ + g_smc_user_table[0xE].handler = NULL; + g_smc_user_table[0xC].id = 0xC300D60C; + g_smc_user_table[0xC].handler = smc_encrypt_rsa_key_for_import; + g_smc_user_table[0xD].handler = smc_decrypt_or_import_rsa_key; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { + /* Nothing to do. */ + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { + /* 1.0.0 doesn't have ConfigureCarveout or ReadWriteRegister. */ + g_smc_priv_table[7].handler = NULL; + g_smc_priv_table[8].handler = NULL; + /* 1.0.0 doesn't have UnwrapAesWrappedTitlekey. */ + g_smc_user_table[0x12].handler = NULL; + } else { + panic_predefined(0xA); } } @@ -306,7 +293,7 @@ void call_smc_handler(uint32_t handler_id, smc_args_t *args) { #endif /* Call function. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800 || + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_8_0_0 || (g_smc_tables[handler_id].handlers[smc_id].blacklist_mask & g_smc_blacklist_mask) == 0) { args->X[0] = smc_handler(args); } else { @@ -636,7 +623,7 @@ uint32_t smc_read_write_register(smc_args_t *args) { return 2; } } else { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { static const uint8_t mc_whitelist_5x[0xD00/(sizeof(uint32_t) * 8)] = { 0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -687,7 +674,7 @@ uint32_t smc_read_write_register(smc_args_t *args) { break; } } - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + 0xD00) { /* Memory Controller RW supported only on 4.0.0+ */ static const uint8_t mc_whitelist[0x68] = { @@ -730,7 +717,7 @@ uint32_t smc_read_write_register(smc_args_t *args) { /* Return old value. */ args->X[1] = old_value; return 0; - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400 && (address == 0x7001923C || address == 0x70019298)) { + } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0 && (address == 0x7001923C || address == 0x70019298)) { /* These addresses are not allowed by the whitelist. */ /* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */ /* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */ @@ -759,7 +746,7 @@ uint32_t smc_configure_carveout(smc_args_t *args) { } /* Configuration is one-shot, and cannot be done multiple times. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_300) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { if (g_configured_carveouts[carveout_id]) { return 2; } diff --git a/exosphere/src/smc_user.c b/exosphere/src/smc_user.c index 7d966b27c..8c0fba6d6 100644 --- a/exosphere/src/smc_user.c +++ b/exosphere/src/smc_user.c @@ -122,23 +122,10 @@ static void validate_rsa_result(unsigned int which) { } static bool is_user_keyslot_valid(unsigned int keyslot) { - switch (exosphere_get_target_firmware()) { - case ATMOSPHERE_TARGET_FIRMWARE_100: - case ATMOSPHERE_TARGET_FIRMWARE_200: - case ATMOSPHERE_TARGET_FIRMWARE_300: - case ATMOSPHERE_TARGET_FIRMWARE_400: - case ATMOSPHERE_TARGET_FIRMWARE_500: - return keyslot <= 3; - case ATMOSPHERE_TARGET_FIRMWARE_600: - case ATMOSPHERE_TARGET_FIRMWARE_620: - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - case ATMOSPHERE_TARGET_FIRMWARE_810: - case ATMOSPHERE_TARGET_FIRMWARE_900: - case ATMOSPHERE_TARGET_FIRMWARE_910: - case ATMOSPHERE_TARGET_FIRMWARE_1000: - default: - return keyslot <= 5; + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { + return keyslot <= 5; + } else { + return keyslot <= 3; } } @@ -262,7 +249,7 @@ uint32_t user_generate_aes_kek(smc_args_t *args) { uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); /* Switches the output based on how it will be used. */ - uint8_t usecase = (uint8_t)((packed_options >> 5) & (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500 ? 7 : 3)); + uint8_t usecase = (uint8_t)((packed_options >> 5) & (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 ? 7 : 3)); /* Switched the output based on whether it should be console unique. */ bool is_personalized = (int)(packed_options & 1); @@ -270,7 +257,7 @@ uint32_t user_generate_aes_kek(smc_args_t *args) { bool is_recovery_boot = configitem_is_recovery_boot(); /* 5.0.0+ Bounds checking. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { if (is_personalized) { if (master_key_rev >= MASTERKEY_REVISION_MAX || (MASTERKEY_REVISION_300 <= master_key_rev && master_key_rev < MASTERKEY_REVISION_400_410)) { return 2; @@ -324,9 +311,9 @@ uint32_t user_generate_aes_kek(smc_args_t *args) { unsigned int keyslot; if (is_personalized) { /* Behavior changed in 4.0.0, and in 5.0.0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { keyslot = devkey_get_keyslot(master_key_rev); - } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_400) { + } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (master_key_rev >= 1) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { @@ -399,7 +386,7 @@ uint32_t user_crypt_aes(smc_args_t *args) { uint32_t keyslot = args->X[1] & 3; uint32_t mode = (args->X[1] >> 4) & 3; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { keyslot = args->X[1] & 7; } @@ -415,7 +402,7 @@ uint32_t user_crypt_aes(smc_args_t *args) { return 2; } - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { /* Disallow dma lists outside of safe range. */ if (in_ll_paddr - 0x80000000 >= 0x3FF7F5) { return 2; @@ -463,7 +450,7 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { if (master_key_rev > 0) { master_key_rev -= 1; } - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { master_key_rev = 0; } @@ -479,9 +466,9 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { unsigned int keyslot; /* Behavior changed in 5.0.0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { keyslot = devkey_get_keyslot(master_key_rev); - } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_400) { + } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (master_key_rev >= 1) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { @@ -560,7 +547,7 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) { upage_ref_t page_ref; /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { generic_panic(); } @@ -609,7 +596,7 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { upage_ref_t page_ref; /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { generic_panic(); } @@ -667,7 +654,7 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { upage_ref_t page_ref; /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { generic_panic(); } @@ -723,7 +710,7 @@ uint32_t user_secure_exp_mod(smc_args_t *args) { void *user_modulus = (void *)args->X[2]; unsigned int exponent_id = 1; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { switch (args->X[3]) { case 0: exponent_id = 1; @@ -753,7 +740,7 @@ uint32_t user_secure_exp_mod(smc_args_t *args) { set_exp_mod_result(3); /* Hardcode RSA keyslot 0. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_1000) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100); se_exp_mod(0, input, 0x100, exp_mod_done_handler); } else if (load_imported_rsa_keypair(0, exponent_id)) { @@ -780,7 +767,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { unsigned int option = (unsigned int)args->X[7]; unsigned int master_key_rev; unsigned int titlekey_type; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_600) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { master_key_rev = option & 0x3F; titlekey_type = (option >> 6) & 1; } else { @@ -792,7 +779,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { master_key_rev -= 1; } - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_300) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { if (master_key_rev >= MASTERKEY_REVISION_MAX) { return 2; } @@ -857,7 +844,7 @@ uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { if (master_key_rev > 0) { master_key_rev -= 1; } - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_300) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { if (master_key_rev >= MASTERKEY_REVISION_MAX) { return 2; } @@ -953,7 +940,7 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) { upage_ref_t page_ref; /* This function only exists in 5.x+. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_500) { + if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { generic_panic(); } @@ -1019,7 +1006,7 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) { } /* Modulus import isn't implemented on < 10.0.0. */ - import_modulus &= (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_1000); + import_modulus &= (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0); /* Import the key. */ import_rsa_exponent(exponent_id, user_data, 0x100); diff --git a/exosphere/src/titlekey.c b/exosphere/src/titlekey.c index 078ed87a9..991123572 100644 --- a/exosphere/src/titlekey.c +++ b/exosphere/src/titlekey.c @@ -40,7 +40,7 @@ void tkey_set_master_key_rev(unsigned int master_key_rev) { } static void tkey_validate_type(unsigned int type) { - if (type > TITLEKEY_TYPE_MAX || (type > 0 && exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_600)) { + if (type > TITLEKEY_TYPE_MAX || (type > 0 && exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_6_0_0)) { generic_panic(); } } diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c index 31e45663e..b7cce98fc 100644 --- a/exosphere/src/warmboot_init.c +++ b/exosphere/src/warmboot_init.c @@ -25,7 +25,7 @@ #undef MC_BASE #define MC_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC)) -#define WARMBOOT_GET_TZRAM_SEGMENT_PA(x) ((g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_500) \ +#define WARMBOOT_GET_TZRAM_SEGMENT_PA(x) ((g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) \ ? TZRAM_GET_SEGMENT_PA(x) : TZRAM_GET_SEGMENT_5X_PA(x)) /* start.s */ @@ -53,7 +53,7 @@ void warmboot_crt0_critical_section_enter(volatile critical_section_t *critical_ } void init_dma_controllers(unsigned int target_firmware) { - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { /* Set some unknown registers in HOST1X. */ MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; MAKE_REG32(0x50003300) = 0; @@ -213,7 +213,7 @@ void warmboot_init(void) { /*identity_remap_tzram();*/ /* Nintendo pointlessly fully invalidate the TLB & invalidate the data cache on the modified ranges here */ - if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_500) { + if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { set_memory_registers_enable_mmu_1x_ttbr0(); } else { set_memory_registers_enable_mmu_5x_ttbr0(); diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c index 7e4f6e2c7..d8ee82e91 100644 --- a/exosphere/src/warmboot_main.c +++ b/exosphere/src/warmboot_main.c @@ -13,7 +13,7 @@ * 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 "mmu.h" #include "memory_map.h" @@ -53,7 +53,7 @@ void __attribute__((noreturn)) warmboot_main(void) { - warmboot (core 3) - cpu_on */ - + if (is_core_active(get_core_id())) { panic(0xF7F00009); /* invalid CPU context */ } @@ -70,7 +70,7 @@ void __attribute__((noreturn)) warmboot_main(void) { clkrst_reboot(CARDEVICE_UARTA); uart_init(UART_A, 115200); } - + if (!configitem_is_retail()) { uart_send(UART_A, "OHAYO", 6); uart_wait_idle(UART_A, UART_VENDOR_STATE_TX_IDLE); @@ -87,15 +87,15 @@ void __attribute__((noreturn)) warmboot_main(void) { /* Make PMC (2.x+), MC (4.x+) registers secure-only */ secure_additional_devices(); - if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) || - ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800) && configitem_get_hardware_type() == 0) || + if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) || + ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_8_0_0) && configitem_get_hardware_type() == 0) || (configitem_is_hiz_mode_enabled())) { warmboot_configure_hiz_mode(); } clear_user_smc_in_progress(); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) { + if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { setup_4x_mmio(); } } diff --git a/fusee/fusee-mtc/src/fuse.c b/fusee/fusee-mtc/src/fuse.c index 1995e1ee3..8ef10e11b 100644 --- a/fusee/fusee-mtc/src/fuse.c +++ b/fusee/fusee-mtc/src/fuse.c @@ -206,7 +206,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -215,7 +215,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; diff --git a/fusee/fusee-primary/src/fuse.c b/fusee/fusee-primary/src/fuse.c index 1995e1ee3..8ef10e11b 100644 --- a/fusee/fusee-primary/src/fuse.c +++ b/fusee/fusee-primary/src/fuse.c @@ -206,7 +206,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -215,7 +215,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; diff --git a/fusee/fusee-secondary/src/device_partition.h b/fusee/fusee-secondary/src/device_partition.h index 03de4ef20..41f034803 100644 --- a/fusee/fusee-secondary/src/device_partition.h +++ b/fusee/fusee-secondary/src/device_partition.h @@ -13,7 +13,7 @@ * 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 FUSEE_DEVICE_PARTITION_H #define FUSEE_DEVICE_PARTITION_H @@ -50,6 +50,7 @@ typedef struct device_partition_t { device_partition_cipher_t read_cipher; /* Cipher for read operations. */ device_partition_cipher_t write_cipher; /* Cipher for write operations. */ DevicePartitionCryptoMode crypto_mode; /* Mode to use for cryptographic operations. */ + size_t crypto_sector_size; device_partition_initializer_t initializer; /* Initializer. */ device_partition_finalizer_t finalizer; /* Finalizer. */ @@ -65,7 +66,7 @@ typedef struct device_partition_t { uint8_t __attribute__((aligned(16))) keys[DEVPART_KEY_MAX][DEVPART_KEY_MAX_SIZE]; /* Key. */ uint8_t __attribute__((aligned(16))) iv[DEVPART_IV_MAX_SIZE]; /* IV. */ bool initialized; - + char *emu_file_path; /* Emulated device file path. */ bool emu_use_file; } device_partition_t; diff --git a/fusee/fusee-secondary/src/fs_dev.c b/fusee/fusee-secondary/src/fs_dev.c index 5dc42b8e5..28b640438 100644 --- a/fusee/fusee-secondary/src/fs_dev.c +++ b/fusee/fusee-secondary/src/fs_dev.c @@ -13,7 +13,7 @@ * 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 <errno.h> #include <limits.h> #include <stdlib.h> @@ -121,7 +121,7 @@ int fsdev_mount_device(const char *name, const device_partition_t *devpart, bool fsdev_device_t *device = fsdev_find_device(name); FRESULT rc; char drname[40]; - + if (device != NULL) { errno = EEXIST; /* Device already exists */ return -1; @@ -170,7 +170,7 @@ int fsdev_mount_device(const char *name, const device_partition_t *devpart, bool VolumeStr[device - g_fsdev_devices] = FKNAM; return fsdev_convert_rc(NULL, rc); } - + device->setup = true; device->registered = false; @@ -289,6 +289,20 @@ int fsdev_unmount_device(const char *name) { return ret; } +int fsdev_register_keys(const char *name, unsigned int target_firmware, BisPartition part) { + fsdev_device_t *device = fsdev_find_device(name); + + if (device == NULL) { + errno = ENOENT; + return -1; + } + + derive_bis_key(device->devpart.keys, part, target_firmware); + + return 0; +} + + int fsdev_unmount_all(void) { for (size_t i = 0; i < FF_VOLUMES; i++) { int ret = fsdev_unmount_device(g_fsdev_devices[i].name); @@ -403,7 +417,7 @@ static void fsdev_filinfo_to_st(struct stat *st, const FILINFO *info) { date.tm_sec = (info->ftime << 1) & 63; date.tm_min = (info->ftime >> 5) & 63; date.tm_hour = (info->ftime >> 11) & 31; - + date.tm_isdst = 0; st->st_atime = st->st_mtime = st->st_ctime = mktime(&date); diff --git a/fusee/fusee-secondary/src/fs_dev.h b/fusee/fusee-secondary/src/fs_dev.h index 45e68cc2b..dfac3cf22 100644 --- a/fusee/fusee-secondary/src/fs_dev.h +++ b/fusee/fusee-secondary/src/fs_dev.h @@ -13,7 +13,7 @@ * 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 FUSEE_FS_DEV_H #define FUSEE_FS_DEV_H @@ -21,12 +21,15 @@ #include <stdint.h> #include <stdbool.h> #include "device_partition.h" +#include "key_derivation.h" int fsdev_mount_device(const char *name, const device_partition_t *devpart, bool initialize_immediately); int fsdev_register_device(const char *name); int fsdev_unregister_device(const char *name); int fsdev_unmount_device(const char *name); /* also unregisters. */ +int fsdev_register_keys(const char *name, unsigned int target_firmware, BisPartition part); + int fsdev_set_attr(const char *file, int attr, int mask); /* Non-standard function to set file DOS attributes. */ int fsdev_get_attr(const char *file); /* Non-standard function to get file DOS attributes. */ diff --git a/fusee/fusee-secondary/src/fs_utils.c b/fusee/fusee-secondary/src/fs_utils.c index 90d521aac..3cc16b193 100644 --- a/fusee/fusee-secondary/src/fs_utils.c +++ b/fusee/fusee-secondary/src/fs_utils.c @@ -13,10 +13,12 @@ * 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 <stdio.h> #include <sys/stat.h> +#include "lib/fatfs/ff.h" #include "fs_utils.h" +#include "fs_dev.h" size_t get_file_size(const char *filename) { struct stat st; @@ -54,7 +56,7 @@ bool is_valid_folder(const char *path) { if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { return true; } - + return false; } @@ -63,6 +65,29 @@ bool is_valid_file(const char *path) { if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { return true; } - + return false; } + +bool is_valid_concatenation_file(const char *path) { + if (is_valid_file(path)) { + return true; + } else if (is_valid_folder(path)) { + /* Check if the archive bit is set. */ + int rc = fsdev_get_attr(path); + + /* Failed to get file DOS attributes. */ + if (rc == -1) { + return false; + } + + /* Check if our path is not a directory (it should be if we're in this code, though...). */ + if (!(rc & AM_DIR)) { + return false; + } + + return (rc & AM_ARC) != 0; + } else { + return false; + } +} diff --git a/fusee/fusee-secondary/src/fs_utils.h b/fusee/fusee-secondary/src/fs_utils.h index 4ca81d193..761632431 100644 --- a/fusee/fusee-secondary/src/fs_utils.h +++ b/fusee/fusee-secondary/src/fs_utils.h @@ -13,7 +13,7 @@ * 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 FUSEE_FS_UTILS_H #define FUSEE_FS_UTILS_H @@ -25,4 +25,6 @@ size_t dump_to_file(const void *src, size_t src_size, const char *filename); bool is_valid_folder(const char *path); bool is_valid_file(const char *path); +bool is_valid_concatenation_file(const char *path); + #endif diff --git a/fusee/fusee-secondary/src/fuse.c b/fusee/fusee-secondary/src/fuse.c index 1995e1ee3..3236a3955 100644 --- a/fusee/fusee-secondary/src/fuse.c +++ b/fusee/fusee-secondary/src/fuse.c @@ -206,7 +206,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -215,7 +215,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; @@ -261,3 +261,12 @@ void fuse_get_hardware_info(void *dst) { memcpy(dst, hw_info, 0x10); } + +/* Get the Key Generation value. */ +uint32_t fuse_get_5x_key_generation(void) { + if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) { + return (fuse_get_reserved_odm(2) & 0x1F); + } else { + return 0; + } +} diff --git a/fusee/fusee-secondary/src/fuse.h b/fusee/fusee-secondary/src/fuse.h index 0e15cbe63..71a4f85aa 100644 --- a/fusee/fusee-secondary/src/fuse.h +++ b/fusee/fusee-secondary/src/fuse.h @@ -219,6 +219,7 @@ uint32_t fuse_get_dram_id(void); uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_retail_type(void); void fuse_get_hardware_info(void *dst); +uint32_t fuse_get_5x_key_generation(void); uint32_t fuse_hw_read(uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr); diff --git a/fusee/fusee-secondary/src/key_derivation.c b/fusee/fusee-secondary/src/key_derivation.c index 1fc808b26..57982b1bf 100644 --- a/fusee/fusee-secondary/src/key_derivation.c +++ b/fusee/fusee-secondary/src/key_derivation.c @@ -15,6 +15,7 @@ */ #include <stdio.h> +#include "lib/log.h" #include "key_derivation.h" #include "masterkey.h" #include "se.h" @@ -143,29 +144,16 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui } /* Do 6.2.0+ keygen. */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_620) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { uint32_t desired_keyblob; - switch (target_firmware) { - case ATMOSPHERE_TARGET_FIRMWARE_620: - desired_keyblob = MASTERKEY_REVISION_620; - break; - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - desired_keyblob = MASTERKEY_REVISION_700_800; - break; - case ATMOSPHERE_TARGET_FIRMWARE_810: - desired_keyblob = MASTERKEY_REVISION_810; - /* Fallthrough */ - case ATMOSPHERE_TARGET_FIRMWARE_900: - desired_keyblob = MASTERKEY_REVISION_900; - /* Fallthrough */ - case ATMOSPHERE_TARGET_FIRMWARE_910: - case ATMOSPHERE_TARGET_FIRMWARE_1000: - desired_keyblob = MASTERKEY_REVISION_910_CURRENT; - break; - default: - fatal_error("Unknown target firmware: %02x!", target_firmware); - break; + + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { + /* NOTE: We load in the current key for all >= 8.1.0 firmwares to reduce sept binaries. */ + desired_keyblob = MASTERKEY_REVISION_910_CURRENT; + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { + desired_keyblob = MASTERKEY_REVISION_700_800; + } else { + desired_keyblob = MASTERKEY_REVISION_620; } /* Try emulation result. */ @@ -213,31 +201,31 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui set_aes_keyslot(0xC, g_dec_keyblobs[available_revision].master_kek, 0x10); /* Also set the Package1 key for the revision that is stored on the eMMC boot0 partition. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_620) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { load_package1_key(available_revision); } /* Derive keys for Exosphere, lock critical keyslots. */ switch (target_firmware) { - case ATMOSPHERE_TARGET_FIRMWARE_100: - case ATMOSPHERE_TARGET_FIRMWARE_200: - case ATMOSPHERE_TARGET_FIRMWARE_300: + case ATMOSPHERE_TARGET_FIRMWARE_1_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_2_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_3_0_0: decrypt_data_into_keyslot(0xD, 0xF, devicekey_seed, 0x10); decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10); break; - case ATMOSPHERE_TARGET_FIRMWARE_400: + case ATMOSPHERE_TARGET_FIRMWARE_4_0_0: decrypt_data_into_keyslot(0xD, 0xF, devicekey_4x_seed, 0x10); decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10); decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10); break; - case ATMOSPHERE_TARGET_FIRMWARE_500: - case ATMOSPHERE_TARGET_FIRMWARE_600: - case ATMOSPHERE_TARGET_FIRMWARE_620: - case ATMOSPHERE_TARGET_FIRMWARE_700: - case ATMOSPHERE_TARGET_FIRMWARE_800: - case ATMOSPHERE_TARGET_FIRMWARE_810: - case ATMOSPHERE_TARGET_FIRMWARE_900: + case ATMOSPHERE_TARGET_FIRMWARE_5_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_6_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_6_2_0: + case ATMOSPHERE_TARGET_FIRMWARE_7_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_8_0_0: + case ATMOSPHERE_TARGET_FIRMWARE_8_1_0: + case ATMOSPHERE_TARGET_FIRMWARE_9_0_0: decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10); decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10); @@ -254,11 +242,13 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui /* Sets final keyslot flags, for handover to TZ/Exosphere. Setting these will prevent the BPMP from using the device key or master key. */ void finalize_nx_keydata(uint32_t target_firmware) { set_aes_keyslot_flags(0xC, 0xFF); - set_aes_keyslot_flags((target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) ? (KEYSLOT_SWITCH_4XOLDDEVICEKEY) : (KEYSLOT_SWITCH_DEVICEKEY), 0xFF); + set_aes_keyslot_flags((target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (KEYSLOT_SWITCH_4XOLDDEVICEKEY) : (KEYSLOT_SWITCH_DEVICEKEY), 0xFF); } -static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware) { - unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) ? (KEYSLOT_SWITCH_4XOLDDEVICEKEY) : (KEYSLOT_SWITCH_DEVICEKEY); +static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware, uint32_t generation) { + unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (devkey_get_keyslot(generation)) : (KEYSLOT_SWITCH_DEVICEKEY); + + if (fuse_get_bootrom_patch_version() < 0x7F) { /* On dev units, use a fixed "all-zeroes" seed. */ /* Yes, this data really is all-zero in actual TrustZone .rodata. */ @@ -281,7 +271,7 @@ static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool s } } -static void generate_personalized_aes_key_for_bis(void *dst, const void *wrapped_kek, const void *wrapped_key, uint32_t target_firmware) { +static void generate_personalized_aes_key_for_bis(void *dst, const void *wrapped_kek, const void *wrapped_key, uint32_t target_firmware, uint32_t generation) { static const uint8_t AL16 kek_source[0x10] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 }; @@ -289,7 +279,7 @@ static void generate_personalized_aes_key_for_bis(void *dst, const void *wrapped 0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8 }; - unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400) ? (KEYSLOT_SWITCH_4XOLDDEVICEKEY) : (KEYSLOT_SWITCH_DEVICEKEY); + unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (devkey_get_keyslot(generation)) : (KEYSLOT_SWITCH_DEVICEKEY); /* Derive kek. */ decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_kek, 0x10); @@ -314,16 +304,18 @@ void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmwa } }; + const uint32_t bis_key_generation = fuse_get_5x_key_generation(); + static const uint8_t AL16 bis_kek_source[0x10] = {0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F}; switch (partition_id) { case BisPartition_Calibration: - generate_specific_aes_key(dst, key_source_for_bis[partition_id][0], false, target_firmware); - generate_specific_aes_key(dst + 0x10, key_source_for_bis[partition_id][1], false, target_firmware); + generate_specific_aes_key(dst, key_source_for_bis[partition_id][0], false, target_firmware, bis_key_generation); + generate_specific_aes_key(dst + 0x10, key_source_for_bis[partition_id][1], false, target_firmware, bis_key_generation); break; case BisPartition_Safe: case BisPartition_UserSystem: - generate_personalized_aes_key_for_bis(dst, bis_kek_source, key_source_for_bis[partition_id][0], target_firmware); - generate_personalized_aes_key_for_bis(dst + 0x10, bis_kek_source, key_source_for_bis[partition_id][1], target_firmware); + generate_personalized_aes_key_for_bis(dst, bis_kek_source, key_source_for_bis[partition_id][0], target_firmware, bis_key_generation); + generate_personalized_aes_key_for_bis(dst + 0x10, bis_kek_source, key_source_for_bis[partition_id][1], target_firmware, bis_key_generation); break; default: generic_panic(); diff --git a/fusee/fusee-secondary/src/lib/log.c b/fusee/fusee-secondary/src/lib/log.c index 1a493ac28..18fef5556 100644 --- a/fusee/fusee-secondary/src/lib/log.c +++ b/fusee/fusee-secondary/src/lib/log.c @@ -91,7 +91,7 @@ static void add_prefix(ScreenLogLevel screen_log_level, const char *fmt, char *b /** * print - logs a message and prints it to screen based on its screen_log_level - * + * * If the level is below g_screen_log_level it will not be shown but logged to UART * Use SCREEN_LOG_LEVEL_NO_PREFIX if you don't want a prefix to be added * UART is TODO diff --git a/fusee/fusee-secondary/src/masterkey.c b/fusee/fusee-secondary/src/masterkey.c index 63ca744fa..4cbcf42cf 100644 --- a/fusee/fusee-secondary/src/masterkey.c +++ b/fusee/fusee-secondary/src/masterkey.c @@ -17,6 +17,7 @@ #include <stdbool.h> #include <stdint.h> #include <string.h> +#include <vapours/ams/ams_target_firmware.h> #include "utils.h" #include "masterkey.h" @@ -26,6 +27,7 @@ static unsigned int g_mkey_revision = 0; static bool g_determined_mkey_revision = false; static uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10]; +static uint8_t g_old_devicekeys[MASTERKEY_NUM_NEW_DEVICE_KEYS - 1][0x10]; /* TODO: Extend with new vectors, as needed. */ /* Dev unit keys. */ @@ -59,6 +61,39 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */ }; +static const uint8_t new_device_key_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { + {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.x New Device Key Source. */ + {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.x New Device Key Source. */ + {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */ + {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */ + {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */ + {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 New Device Key Source. */ + {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 New Device Key Source. */ + {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 New Device Key Source. */ +}; + +static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { + {0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.x New Device Keygen Source. */ + {0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.x New Device Keygen Source. */ + {0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.x New Device Keygen Source. */ + {0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 New Device Keygen Source. */ + {0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 New Device Keygen Source. */ + {0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 New Device Keygen Source. */ + {0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 New Device Keygen Source. */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ +}; + +static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { + {0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34}, /* 4.x New Device Keygen Source. */ + {0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8}, /* 5.x New Device Keygen Source. */ + {0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.x New Device Keygen Source. */ + {0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 New Device Keygen Source. */ + {0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 New Device Keygen Source. */ + {0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 New Device Keygen Source. */ + {0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 New Device Keygen Source. */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ +}; + static bool check_mkey_revision(unsigned int revision, bool is_retail) { uint8_t final_vector[0x10]; @@ -127,3 +162,50 @@ unsigned int mkey_get_keyslot(unsigned int revision) { return KEYSLOT_SWITCH_TEMPKEY; } } + +void derive_new_device_keys(bool is_retail, unsigned int keygen_keyslot, unsigned int target_firmware) { + uint8_t work_buffer[0x10]; + for (unsigned int revision = 0; revision < MASTERKEY_NUM_NEW_DEVICE_KEYS; revision++) { + const unsigned int relative_revision = revision + MASTERKEY_REVISION_400_410; + + se_aes_ecb_decrypt_block(keygen_keyslot, work_buffer, 0x10, new_device_key_sources[revision], 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, mkey_get_keyslot(0), is_retail ? new_device_keygen_sources[revision] : new_device_keygen_sources_dev[revision], 0x10); + if (relative_revision > mkey_get_revision()) { + break; + } else if (relative_revision == mkey_get_revision()) { + /* On 7.0.0, sept will have derived this key for us already. */ + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10); + } + } else { + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10, work_buffer, 0x10); + set_old_devkey(relative_revision, work_buffer); + } + } + set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); + clear_aes_keyslot(keygen_keyslot); +} + +void set_old_devkey(unsigned int revision, const uint8_t *key) { + if (revision < MASTERKEY_REVISION_400_410 || MASTERKEY_REVISION_MAX <= revision) { + generic_panic(); + } + + memcpy(g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], key, 0x10); +} + +unsigned int devkey_get_keyslot(unsigned int revision) { + if (!g_determined_mkey_revision || revision > g_mkey_revision) { + generic_panic(); + } + + if (revision < MASTERKEY_REVISION_400_410) { + return KEYSLOT_SWITCH_4XOLDDEVICEKEY; + } else if (revision < g_mkey_revision) { + /* Load into a temp keyslot. */ + set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], 0x10); + return KEYSLOT_SWITCH_TEMPKEY; + } else { + return KEYSLOT_SWITCH_DEVICEKEY; + } +} diff --git a/fusee/fusee-secondary/src/masterkey.h b/fusee/fusee-secondary/src/masterkey.h index a15b19a1c..45d213970 100644 --- a/fusee/fusee-secondary/src/masterkey.h +++ b/fusee/fusee-secondary/src/masterkey.h @@ -41,7 +41,11 @@ int mkey_detect_revision(bool is_retail); unsigned int mkey_get_revision(void); unsigned int mkey_get_keyslot(unsigned int revision); + +void derive_new_device_keys(bool is_retail, unsigned int keygen_keyslot, unsigned int target_firmware); void set_old_devkey(unsigned int revision, const uint8_t *key); unsigned int devkey_get_keyslot(unsigned int revision); + + #endif \ No newline at end of file diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index d1e919839..07974428f 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -218,41 +218,110 @@ static int stratosphere_ini_handler(void *user, const char *section, const char return 1; } +static bool is_nca_present(const char *nca_name) { + char path[0x100]; + snprintf(path, sizeof(path), "system:/contents/registered/%s.nca", nca_name); + + return is_valid_concatenation_file(path); +} + + +static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ + #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) + + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + CHECK_NCA("e951bc9dedcd54f65ffd83d4d050f9e0", 10_0_2); + CHECK_NCA("36ab1acf0c10a2beb9f7d472685f9a89", 10_0_1); + CHECK_NCA("5625cdc21d5f1ca52f6c36ba261505b9", 10_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_1_0) { + CHECK_NCA("09ef4d92bb47b33861e695ba524a2c17", 9_2_0); + CHECK_NCA("c5fbb49f2e3648c8cfca758020c53ecb", 9_1_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0) { + CHECK_NCA("fd1ffb82dc1da76346343de22edbc97c", 9_0_1); + CHECK_NCA("a6af05b33f8f903aab90c8b0fcbcc6a4", 9_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { + CHECK_NCA("724d9b432929ea43e787ad81bf09ae65", 8_1_1); /* 8.1.1-100 from Lite */ + CHECK_NCA("e9bb0602e939270a9348bddd9b78827b", 8_1_1); /* 8.1.1-12 from chinese gamecard */ + CHECK_NCA("7eedb7006ad855ec567114be601b2a9d", 8_1_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0) { + CHECK_NCA("6c5426d27c40288302ad616307867eba", 8_0_1); + CHECK_NCA("4fe7b4abcea4a0bcc50975c1a926efcb", 8_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { + CHECK_NCA("e6b22c40bb4fa66a151f1dc8db5a7b5c", 7_0_1); + CHECK_NCA("c613bd9660478de69bc8d0e2e7ea9949", 7_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { + CHECK_NCA("6dfaaf1a3cebda6307aa770d9303d9b6", 6_2_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { + CHECK_NCA("1d21680af5a034d626693674faf81b02", 6_1_0); + CHECK_NCA("663e74e45ffc86fbbaeb98045feea315", 6_0_1); + CHECK_NCA("258c1786b0f6844250f34d9c6f66095b", 6_0_0); /* Release 6.0.0-5.0 */ + CHECK_NCA("286e30bafd7e4197df6551ad802dd815", 6_0_0); /* Pre-Release 6.0.0-4.0 */ + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { + CHECK_NCA("fce3b0ea366f9c95fe6498b69274b0e7", 5_1_0); + CHECK_NCA("c5758b0cb8c6512e8967e38842d35016", 5_0_2); + CHECK_NCA("53eb605d4620e8fd50064b24fd57783a", 5_0_1); + CHECK_NCA("09a2f9c16ce1c121ae6d231b35d17515", 5_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + CHECK_NCA("77e1ae7661ad8a718b9b13b70304aeea", 4_1_0); + CHECK_NCA("d0e5d20e3260f3083bcc067483b71274", 4_0_1); + CHECK_NCA("483a24ee3fd7149f9112d1931166a678", 4_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { + CHECK_NCA("704129fc89e1fcb85c37b3112e51b0fc", 3_0_2); + CHECK_NCA("1fb00543307337d523ccefa9923e0c50", 3_0_1); + CHECK_NCA("6ebd3447473bade18badbeb5032af87d", 3_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { + CHECK_NCA("d1c991c53a8a9038f8c3157a553d876d", 2_3_0); + CHECK_NCA("7f90353dff2d7ce69e19e07ebc0d5489", 2_2_0); + CHECK_NCA("e9b3e75fce00e52fe646156634d229b4", 2_1_0); + CHECK_NCA("7a1f79f8184d4b9bae1755090278f52c", 2_0_0); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { + CHECK_NCA("a1b287e07f8455e8192f13d0e45a2aaf", 1_0_0); /* 1.0.0 from Factory */ + CHECK_NCA("117f7b9c7da3e8cef02340596af206b3", 1_0_0); /* 1.0.0 from Gamecard */ + } else { + fatal_error("[NXBOOT] Unknown Target Firmware!"); + } + + #undef CHECK_NCA + + /* If we didn't find a more specific firmware, return our package1 approximation. */ + return target_firmware; +} + static uint32_t nxboot_get_target_firmware(const void *package1loader) { const package1loader_header_t *package1loader_header = (const package1loader_header_t *)package1loader; switch (package1loader_header->version) { case 0x01: /* 1.0.0 */ - return ATMOSPHERE_TARGET_FIRMWARE_100; + return ATMOSPHERE_TARGET_FIRMWARE_1_0_0; case 0x02: /* 2.0.0 - 2.3.0 */ - return ATMOSPHERE_TARGET_FIRMWARE_200; + return ATMOSPHERE_TARGET_FIRMWARE_2_0_0; case 0x04: /* 3.0.0 and 3.0.1 - 3.0.2 */ - return ATMOSPHERE_TARGET_FIRMWARE_300; + return ATMOSPHERE_TARGET_FIRMWARE_3_0_0; case 0x07: /* 4.0.0 - 4.1.0 */ - return ATMOSPHERE_TARGET_FIRMWARE_400; + return ATMOSPHERE_TARGET_FIRMWARE_4_0_0; case 0x0B: /* 5.0.0 - 5.1.0 */ - return ATMOSPHERE_TARGET_FIRMWARE_500; + return ATMOSPHERE_TARGET_FIRMWARE_5_0_0; case 0x0E: { /* 6.0.0 - 6.2.0 */ if (memcmp(package1loader_header->build_timestamp, "20180802", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_600; + return ATMOSPHERE_TARGET_FIRMWARE_6_0_0; } else if (memcmp(package1loader_header->build_timestamp, "20181107", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_620; + return ATMOSPHERE_TARGET_FIRMWARE_6_2_0; } else { fatal_error("[NXBOOT] Unable to identify package1!\n"); } } case 0x0F: /* 7.0.0 - 7.0.1 */ - return ATMOSPHERE_TARGET_FIRMWARE_700; + return ATMOSPHERE_TARGET_FIRMWARE_7_0_0; case 0x10: { /* 8.0.0 - 9.0.0 */ if (memcmp(package1loader_header->build_timestamp, "20190314", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_800; + return ATMOSPHERE_TARGET_FIRMWARE_8_0_0; } else if (memcmp(package1loader_header->build_timestamp, "20190531", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_810; + return ATMOSPHERE_TARGET_FIRMWARE_8_1_0; } else if (memcmp(package1loader_header->build_timestamp, "20190809", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_900; + return ATMOSPHERE_TARGET_FIRMWARE_9_0_0; } else if (memcmp(package1loader_header->build_timestamp, "20191021", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_910; + return ATMOSPHERE_TARGET_FIRMWARE_9_1_0; } else if (memcmp(package1loader_header->build_timestamp, "20200303", 8) == 0) { - return ATMOSPHERE_TARGET_FIRMWARE_1000; + return ATMOSPHERE_TARGET_FIRMWARE_10_0_0; } else { fatal_error("[NXBOOT] Unable to identify package1!\n"); } @@ -424,11 +493,11 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) { } } else { /* Check if fuses are < 4.0.0, but firmware is >= 4.0.0 */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_400 && !(fuse_get_reserved_odm(7) & ~0x0000000F)) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0 && !(fuse_get_reserved_odm(7) & ~0x0000000F)) { kip_patches_set_enable_nogc(); } /* Check if the fuses are < 9.0.0, but firmware is >= 9.0.0 */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_900 && !(fuse_get_reserved_odm(7) & ~0x000003FF)) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0 && !(fuse_get_reserved_odm(7) & ~0x000003FF)) { kip_patches_set_enable_nogc(); } } @@ -516,8 +585,8 @@ static void nxboot_move_bootconfig() { fclose(bcfile); /* Select the actual BootConfig size and destination address. */ - bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_600) ? 0x4003D000 : 0x4003F800; - bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) ? 0x3000 : 0x1000; + bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) ? 0x4003D000 : 0x4003F800; + bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? 0x3000 : 0x1000; /* Copy the BootConfig into IRAM. */ memset((void *)bootconfig_addr, 0, bootconfig_size); @@ -638,6 +707,7 @@ uint32_t nxboot_main(void) { /* Find the system's target firmware. */ uint32_t target_firmware = nxboot_get_target_firmware(package1loader); + if (!target_firmware) fatal_error("[NXBOOT] Failed to detect target firmware!\n"); else @@ -685,7 +755,7 @@ uint32_t nxboot_main(void) { if (!package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size)) { fatal_error("[NXBOOT] Failed to read the TSEC firmware from Package1loader!\n"); } - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_810) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { if (fuse_get_retail_type() != 0) { sept_secondary_enc = sept_secondary_01_enc; sept_secondary_enc_size = sept_secondary_01_enc_size; @@ -694,7 +764,7 @@ uint32_t nxboot_main(void) { sept_secondary_enc_size = sept_secondary_dev_01_enc_size; } tsec_fw_size = 0x3300; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) { + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { if (fuse_get_retail_type() != 0) { sept_secondary_enc = sept_secondary_00_enc; sept_secondary_enc_size = sept_secondary_00_enc_size; @@ -703,7 +773,7 @@ uint32_t nxboot_main(void) { sept_secondary_enc_size = sept_secondary_dev_00_enc_size; } tsec_fw_size = 0x3000; - } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_620) { + } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { tsec_fw_size = 0x2900; } else { tsec_fw_size = 0xF00; @@ -715,7 +785,7 @@ uint32_t nxboot_main(void) { /* Get the TSEC keys. */ uint8_t tsec_key[0x10] = {0}; uint8_t tsec_root_keys[0x20][0x10] = {0}; - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Detect whether we need to run sept-secondary in order to derive keys. */ if (!get_and_clear_has_run_sept()) { reboot_to_sept(tsec_fw, tsec_fw_size, sept_secondary_enc, sept_secondary_enc_size); @@ -725,7 +795,7 @@ uint32_t nxboot_main(void) { } } get_and_clear_has_run_sept(); - } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_620) { + } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { uint8_t tsec_keys[0x20] = {0}; /* Emulate the TSEC payload on 6.2.0+. */ @@ -746,17 +816,46 @@ uint32_t nxboot_main(void) { /* Derive keydata. If on 7.0.0+, sept has already derived keys for us. */ unsigned int keygen_type = 0; - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { if (derive_nx_keydata(target_firmware, g_keyblobs, available_revision, tsec_key, tsec_root_keys, &keygen_type) != 0) { fatal_error("[NXBOOT] Key derivation failed!\n"); } } + /* Derive new device keys. */ + { + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { + derive_new_device_keys(fuse_get_retail_type() != 0, KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY, target_firmware); + } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { + derive_new_device_keys(fuse_get_retail_type() != 0, KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY, target_firmware); + } else { + /* No new keys to derive */ + } + } + + /* Set the system partition's keys. */ + if (fsdev_register_keys("system", target_firmware, BisPartition_UserSystem) != 0) { + fatal_error("[NXBOOT] Failed to set SYSTEM partition keys!\n"); + } + + /* Mount the system partition. */ + if (fsdev_register_device("system") != 0) { + fatal_error("[NXBOOT] Failed to register SYSTEM partition!\n"); + } + + /* Lightly validate the system partition. */ + if (!is_valid_folder("system:/Contents")) { + fatal_error("[NXBOOT] SYSTEM partition seems corrupted!\n"); + } + + /* Make the target firmware more specific. */ + target_firmware = nxboot_get_specific_target_firmware(target_firmware); + /* Setup boot configuration for Exosphère. */ nxboot_configure_exosphere(target_firmware, keygen_type, &exo_emummc_cfg); /* Initialize Boot Reason on older firmware versions. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Initializing Boot Reason...\n"); nxboot_set_bootreason((void *)MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(target_firmware)); } @@ -807,11 +906,11 @@ uint32_t nxboot_main(void) { } /* Select the right address for the warmboot firmware. */ - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { warmboot_memaddr = (void *)0x8000D000; - } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_600) { + } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { warmboot_memaddr = (void *)0x4003B000; - } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { + } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { warmboot_memaddr = (void *)0x4003D800; } else { warmboot_memaddr = (void *)0x4003E000; @@ -822,7 +921,7 @@ uint32_t nxboot_main(void) { /* Copy the warmboot firmware and set the address in PMC if necessary. */ if (warmboot_fw && (warmboot_fw_size > 0)) { memcpy(warmboot_memaddr, warmboot_fw, warmboot_fw_size); - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) + if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) pmc->scratch1 = (uint32_t)warmboot_memaddr; } @@ -842,7 +941,7 @@ uint32_t nxboot_main(void) { print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Reading Exosphère...\n"); /* Select the right address for Exosphère. */ - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { exosphere_memaddr = (void *)0x4002D000; } else { exosphere_memaddr = (void *)0x4002B000; @@ -870,7 +969,7 @@ uint32_t nxboot_main(void) { nxboot_move_bootconfig(); /* Set 3.0.0/3.0.1/3.0.2 warmboot security check. */ - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware == ATMOSPHERE_TARGET_FIRMWARE_300) { + if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware == ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { const package1loader_header_t *package1loader_header = (const package1loader_header_t *)package1loader; if (!strcmp(package1loader_header->build_timestamp, "20170519101410")) pmc->secure_scratch32 = 0xE3; /* Warmboot 3.0.0 security check.*/ diff --git a/fusee/fusee-secondary/src/nxboot.h b/fusee/fusee-secondary/src/nxboot.h index 8627f874d..8c640e2f5 100644 --- a/fusee/fusee-secondary/src/nxboot.h +++ b/fusee/fusee-secondary/src/nxboot.h @@ -13,7 +13,7 @@ * 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 FUSEE_NX_BOOT_H #define FUSEE_NX_BOOT_H @@ -35,7 +35,7 @@ typedef struct { #define MAILBOX_NX_BOOTLOADER_BASE_100_620 0x40002E00 #define MAILBOX_NX_BOOTLOADER_BASE_700 0x40000000 -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_700) ? (MAILBOX_NX_BOOTLOADER_BASE_700) : (MAILBOX_NX_BOOTLOADER_BASE_100_620)) +#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (MAILBOX_NX_BOOTLOADER_BASE_700) : (MAILBOX_NX_BOOTLOADER_BASE_100_620)) #define MAKE_MAILBOX_NX_BOOTLOADER_REG(targetfw, n) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + n) #define MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(targetfw) (MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0x10) diff --git a/fusee/fusee-secondary/src/nxboot_iram.c b/fusee/fusee-secondary/src/nxboot_iram.c index 8082de760..bc40d9afd 100644 --- a/fusee/fusee-secondary/src/nxboot_iram.c +++ b/fusee/fusee-secondary/src/nxboot_iram.c @@ -13,7 +13,7 @@ * 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 <string.h> #include "cluster.h" @@ -30,40 +30,40 @@ void nxboot_finish(uint32_t boot_memaddr) { uint32_t target_firmware = MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware; volatile tegra_se_t *se = se_get_regs(); - + /* Clear used keyslots. */ clear_aes_keyslot(KEYSLOT_SWITCH_PACKAGE2KEY); clear_aes_keyslot(KEYSLOT_SWITCH_RNGKEY); - + /* Lock keyslots. */ set_aes_keyslot_flags(KEYSLOT_SWITCH_MASTERKEY, 0xFF); - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); } else { set_aes_keyslot_flags(KEYSLOT_SWITCH_4XOLDDEVICEKEY, 0xFF); } - + /* Finalize the GPU UCODE carveout. */ /* NOTE: [4.0.0+] This is now done in the Secure Monitor. */ /* mc_config_carveout_finalize(); */ - + /* Lock AES keyslots. */ for (uint32_t i = 0; i < 16; i++) set_aes_keyslot_flags(i, 0x15); - + /* Lock RSA keyslots. */ for (uint32_t i = 0; i < 2; i++) set_rsa_keyslot_flags(i, 1); - + /* Lock the Security Engine. */ se->SE_TZRAM_SECURITY = 0; se->SE_CRYPTO_SECURITY_PERKEY = 0; se->SE_RSA_SECURITY_PERKEY = 0; se->SE_SE_SECURITY &= 0xFFFFFFFB; - + /* Boot up Exosphère. */ MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(target_firmware) = 0; - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_LOADED_PACKAGE2; } else { MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X; @@ -71,30 +71,30 @@ void nxboot_finish(uint32_t boot_memaddr) { /* Terminate the display. */ display_end(); - + /* Check if SMMU emulation has been used. */ uint32_t smmu_magic = *(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xFC); if (smmu_magic == 0xDEADC0DE) { /* Clear the magic. */ *(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xFC) = 0; - + /* Pass the boot address to the already running payload. */ *(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xF0) = boot_memaddr; - + /* Wait a while. */ mdelay(500); } else { /* Boot CPU0. */ cluster_boot_cpu0(boot_memaddr); } - + /* Wait for Exosphère to wake up. */ while (MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(target_firmware) == 0) { udelay(1); } - + /* Signal Exosphère. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_FINISHED; } else { MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_FINISHED_4X; diff --git a/fusee/fusee-secondary/src/nxfs.c b/fusee/fusee-secondary/src/nxfs.c index 735e58ca2..8758085a4 100644 --- a/fusee/fusee-secondary/src/nxfs.c +++ b/fusee/fusee-secondary/src/nxfs.c @@ -13,7 +13,7 @@ * 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 <stdio.h> #include <stdlib.h> #include <malloc.h> @@ -24,8 +24,10 @@ #include "gpt.h" #include "se.h" #include "utils.h" +#include "fs_utils.h" #include "sdmmc/sdmmc.h" +#include "lib/log.h" #include "lib/fatfs/ff.h" static bool g_ahb_redirect_enabled = false; @@ -59,7 +61,7 @@ SdmmcPartitionNum g_current_emmc_partition = SDMMC_PARTITION_INVALID; static int mmc_partition_initialize(device_partition_t *devpart) { mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; - + /* Allocate the crypto work buffer. */ if ((devpart->read_cipher != NULL) || (devpart->write_cipher != NULL)) { devpart->crypto_work_buffer = memalign(16, devpart->sector_size * 16); @@ -105,7 +107,7 @@ static int mmc_partition_initialize(device_partition_t *devpart) { static void mmc_partition_finalize(device_partition_t *devpart) { mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; - + /* Finalize hardware. */ if (mmcpart->device == &g_sd_device) { if (g_is_emummc) { @@ -123,13 +125,13 @@ static void mmc_partition_finalize(device_partition_t *devpart) { } devpart->initialized = false; } - + /* Disable AHB redirection if necessary. */ if (g_ahb_redirect_enabled) { mc_disable_ahb_redirect(); g_ahb_redirect_enabled = false; } - + /* Free the crypto work buffer. */ if (devpart->crypto_work_buffer != NULL) { free(devpart->crypto_work_buffer); @@ -138,19 +140,19 @@ static void mmc_partition_finalize(device_partition_t *devpart) { static int mmc_partition_read(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors) { mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; - + if ((mmcpart->device == &g_emmc_device) && (g_current_emmc_partition != mmcpart->partition)) { if (!sdmmc_mmc_select_partition(mmcpart->device, mmcpart->partition)) return EIO; g_current_emmc_partition = mmcpart->partition; } - + return sdmmc_device_read(mmcpart->device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, dst) ? 0 : EIO; } static int mmc_partition_write(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors) { mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct; - + if ((mmcpart->device == &g_emmc_device) && (g_current_emmc_partition != mmcpart->partition)) { if (!sdmmc_mmc_select_partition(mmcpart->device, mmcpart->partition)) return EIO; @@ -174,11 +176,11 @@ static int emummc_partition_initialize(device_partition_t *devpart) { devpart->crypto_work_buffer_num_sectors = 0; } devpart->initialized = true; - + return 0; } -static void emummc_partition_finalize(device_partition_t *devpart) { +static void emummc_partition_finalize(device_partition_t *devpart) { /* Free the crypto work buffer. */ if (devpart->crypto_work_buffer != NULL) { free(devpart->crypto_work_buffer); @@ -194,10 +196,10 @@ static int emummc_partition_read(device_partition_t *devpart, void *dst, uint64_ rc = (fread(dst, devpart->sector_size, num_sectors, emummc_file) > 0) ? 0 : -1; fclose(emummc_file); return rc; - } else { + } else { /* Read partition data directly from the SD card device. */ return sdmmc_device_read(&g_sd_device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, dst) ? 0 : EIO; - } + } } static int emummc_partition_write(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors) { @@ -212,7 +214,7 @@ static int emummc_partition_write(device_partition_t *devpart, const void *src, } else { /* Write partition data directly to the SD card device. */ return sdmmc_device_write(&g_sd_device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, (void *)src) ? 0 : EIO; - } + } } static int nxfs_bis_crypto_decrypt(device_partition_t *devpart, uint64_t sector, uint64_t num_sectors) { @@ -227,7 +229,7 @@ static int nxfs_bis_crypto_decrypt(device_partition_t *devpart, uint64_t sector, case DevicePartitionCryptoMode_Xts: set_aes_keyslot(keyslot_a, devpart->keys[0], 0x10); set_aes_keyslot(keyslot_b, devpart->keys[1], 0x10); - se_aes_128_xts_nintendo_decrypt(keyslot_a, keyslot_b, sector, devpart->crypto_work_buffer, devpart->crypto_work_buffer, size, devpart->sector_size); + se_aes_128_xts_nintendo_decrypt(keyslot_a, keyslot_b, sector, devpart->crypto_work_buffer, devpart->crypto_work_buffer, size, devpart->sector_size, devpart->crypto_sector_size); return 0; case DevicePartitionCryptoMode_None: default: @@ -247,7 +249,7 @@ static int nxfs_bis_crypto_encrypt(device_partition_t *devpart, uint64_t sector, case DevicePartitionCryptoMode_Xts: set_aes_keyslot(keyslot_a, devpart->keys[0], 0x10); set_aes_keyslot(keyslot_b, devpart->keys[1], 0x10); - se_aes_128_xts_nintendo_encrypt(keyslot_a, keyslot_b, sector, devpart->crypto_work_buffer, devpart->crypto_work_buffer, size, devpart->sector_size); + se_aes_128_xts_nintendo_encrypt(keyslot_a, keyslot_b, sector, devpart->crypto_work_buffer, devpart->crypto_work_buffer, size, devpart->sector_size, devpart->crypto_sector_size); return 0; case DevicePartitionCryptoMode_None: default: @@ -256,6 +258,7 @@ static int nxfs_bis_crypto_encrypt(device_partition_t *devpart, uint64_t sector, } static const device_partition_t g_mmc_devpart_template = { + .crypto_sector_size = 0x4000, .sector_size = 512, .initializer = mmc_partition_initialize, .finalizer = mmc_partition_finalize, @@ -264,6 +267,7 @@ static const device_partition_t g_mmc_devpart_template = { }; static const device_partition_t g_emummc_devpart_template = { + .crypto_sector_size = 0x4000, .sector_size = 512, .initializer = emummc_partition_initialize, .finalizer = emummc_partition_finalize, @@ -378,7 +382,7 @@ static int nxfs_mount_emu_partition_gpt_callback(const efi_entry_t *entry, void {"BCPKG2-5-Repair-Main", "bcpkg25", false, false, false}, {"BCPKG2-6-Repair-Sub", "bcpkg26", false, false, false}, {"SAFE", "safe", true, true, false}, - {"SYSTEM", "system", true, true, false}, + {"SYSTEM", "system", true, true, true}, {"USER", "user", true, true, false}, }; @@ -442,26 +446,26 @@ int nxfs_mount_sd() { model.device_struct = &g_sd_mmcpart; model.start_sector = 0; model.num_sectors = 1u << 30; /* arbitrary numbers of sectors. TODO: find the size of the SD in sectors. */ - + /* Mount the SD card device. */ rc = fsdev_mount_device("sdmc", &model, true); - + if (rc == -1) { return -1; } - + /* Register the SD card device. */ rc = fsdev_register_device("sdmc"); - + if (rc == -1) { return -1; } - + /* All fs devices are ready. */ if (rc == 0) { g_fsdev_ready = true; } - + return rc; } @@ -469,36 +473,36 @@ int nxfs_mount_emmc() { device_partition_t model; int rc; FILE *rawnand; - + /* Setup a template for boot0. */ model = g_mmc_devpart_template; model.device_struct = &g_emmc_boot0_mmcpart; model.start_sector = 0; model.num_sectors = 0x184000 / model.sector_size; - + /* Mount boot0 device. */ rc = rawdev_mount_device("boot0", &model, true); - + if (rc == -1) { return -1; } - + /* Register boot0 device. */ rc = rawdev_register_device("boot0"); - + if (rc == -1) { return -1; } - + /* Setup a template for boot1. */ model = g_mmc_devpart_template; model.device_struct = &g_emmc_boot1_mmcpart; model.start_sector = 0; model.num_sectors = 0x80000 / model.sector_size; - + /* Mount boot1 device. */ rc = rawdev_mount_device("boot1", &model, false); - + if (rc == -1) { return -1; } @@ -510,38 +514,38 @@ int nxfs_mount_emmc() { model.device_struct = &g_emmc_user_mmcpart; model.start_sector = 0; model.num_sectors = (256ull << 30) / model.sector_size; - + /* Mount raw NAND device. */ rc = rawdev_mount_device("rawnand", &model, false); - + if (rc == -1) { return -1; } - + /* Register raw NAND device. */ rc = rawdev_register_device("rawnand"); if (rc == -1) { return -1; } - + /* Open raw NAND device. */ rawnand = fopen("rawnand:/", "rb"); - + if (rawnand == NULL) { return -1; } - + /* Iterate the GPT and mount each raw NAND partition. */ rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model); - + /* Close raw NAND device. */ fclose(rawnand); - + /* All raw devices are ready. */ if (rc == 0) { g_rawdev_ready = true; } - + return rc; } @@ -549,7 +553,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) { device_partition_t model; int rc; FILE *rawnand; - + /* Setup an emulation template for boot0. */ model = g_emummc_devpart_template; model.start_sector = emummc_start_sector + (0x400000 * 0 / model.sector_size); @@ -558,20 +562,20 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) { /* Mount emulated boot0 device. */ rc = emudev_mount_device("boot0", &model, NULL, 0, 0); - + /* Failed to mount boot0 device. */ if (rc == -1) { return -1; } - + /* Register emulated boot0 device. */ rc = emudev_register_device("boot0"); - + /* Failed to register boot0 device. */ if (rc == -1) { return -2; } - + /* Setup an emulation template for boot1. */ model = g_emummc_devpart_template; model.start_sector = emummc_start_sector + (0x400000 * 1 / model.sector_size); @@ -580,7 +584,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) { /* Mount emulated boot1 device. */ rc = emudev_mount_device("boot1", &model, NULL, 0, 0); - + /* Failed to mount boot1. */ if (rc == -1) { return -3; @@ -593,43 +597,43 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) { model.start_sector = emummc_start_sector + (0x400000 * 2 / model.sector_size); model.num_sectors = (256ull << 30) / model.sector_size; model.emu_use_file = false; - + /* Mount emulated raw NAND device. */ rc = emudev_mount_device("rawnand", &model, NULL, 0, 0); - + /* Failed to mount raw NAND. */ if (rc == -1) { return -4; } - + /* Register emulated raw NAND device. */ rc = emudev_register_device("rawnand"); - + /* Failed to register raw NAND device. */ if (rc == -1) { return -5; } - + /* Open emulated raw NAND device. */ rawnand = fopen("rawnand:/", "rb"); - + /* Failed to open emulated raw NAND device. */ if (rawnand == NULL) { return -6; } - + /* Iterate the GPT and mount each emulated raw NAND partition. */ rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, NULL, 0, 0); /* Close emulated raw NAND device. */ fclose(rawnand); - + /* All emulated devices are ready. */ if (rc == 0) { g_emudev_ready = true; g_is_emummc = true; } - + return rc; } @@ -640,85 +644,85 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part bool is_exfat; char emummc_boot0_path[0x300 + 1] = {0}; char emummc_boot1_path[0x300 + 1] = {0}; - + /* Check if the SD card is EXFAT formatted. */ rc = fsdev_is_exfat("sdmc"); - + /* Failed to detect file system type. */ if (rc == -1) { return -1; } - + /* Set EXFAT status. */ is_exfat = (rc == 1); - + /* Reject single part in FAT32. */ /* NOTE: This check has no effect in the current design. */ if (!is_exfat && (num_parts < 1)) { return -2; } - + /* We want a folder with the archive bit set. */ rc = fsdev_get_attr(emummc_path); - + /* Failed to get file DOS attributes. */ if (rc == -1) { return -3; } - + /* Our path is not a directory. */ if (!(rc & AM_DIR)) { return -4; } - + /* Check if the archive bit is not set. */ if (!(rc & AM_ARC)) { /* Try to set the archive bit. */ rc = fsdev_set_attr(emummc_path, AM_ARC, AM_ARC); - + /* Failed to set file DOS attributes. */ if (rc == -1) { return -5; } } - + /* Setup an emulation template for boot0. */ model = g_emummc_devpart_template; model.start_sector = 0; model.num_sectors = 0x400000 / model.sector_size; model.emu_use_file = true; - + /* Prepare boot0 file path. */ snprintf(emummc_boot0_path, sizeof(emummc_boot0_path) - 1, "%s/%s", emummc_path, "boot0"); - + /* Mount emulated boot0 device. */ rc = emudev_mount_device("boot0", &model, emummc_boot0_path, 0, 0); - + /* Failed to mount boot0 device. */ if (rc == -1) { return -6; } - + /* Register emulated boot0 device. */ rc = emudev_register_device("boot0"); - + /* Failed to register boot0 device. */ if (rc == -1) { return -7; } - + /* Setup an emulation template for boot1. */ model = g_emummc_devpart_template; model.start_sector = 0; model.num_sectors = 0x400000 / model.sector_size; model.emu_use_file = true; - + /* Prepare boot1 file path. */ snprintf(emummc_boot1_path, sizeof(emummc_boot1_path) - 1, "%s/%s", emummc_path, "boot1"); - + /* Mount emulated boot1 device. */ rc = emudev_mount_device("boot1", &model, emummc_boot1_path, 0, 0); - + /* Failed to mount boot1. */ if (rc == -1) { return -8; @@ -726,7 +730,7 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part /* Register emulated boot1 device. */ rc = emudev_register_device("boot1"); - + /* Failed to register boot1 device. */ if (rc == -1) { return -9; @@ -737,93 +741,93 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part model.start_sector = 0; model.num_sectors = (256ull << 30) / model.sector_size; model.emu_use_file = true; - + /* Mount emulated raw NAND device from single or multiple parts. */ rc = emudev_mount_device("rawnand", &model, emummc_path, num_parts, part_limit); - + /* Failed to mount raw NAND. */ if (rc == -1) { return -10; } - + /* Register emulated raw NAND device. */ rc = emudev_register_device("rawnand"); - + /* Failed to register raw NAND device. */ if (rc == -1) { return -11; } - + /* Open emulated raw NAND device. */ rawnand = fopen("rawnand:/", "rb"); - + /* Failed to open emulated raw NAND device. */ if (rawnand == NULL) { return -12; } - + /* Iterate the GPT and mount each emulated raw NAND partition. */ rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, emummc_path, num_parts, part_limit); /* Close emulated raw NAND device. */ fclose(rawnand); - + /* All emulated devices are ready. */ if (rc == 0) { g_emudev_ready = true; g_is_emummc = true; } - + return rc; } int nxfs_unmount_sd() { int rc = 0; - + /* Unmount all fs devices. */ if (g_fsdev_ready) { rc = fsdev_unmount_all(); g_fsdev_ready = false; } - + return rc; } int nxfs_unmount_emmc() { int rc = 0; - + /* Unmount all raw devices. */ if (g_rawdev_ready) { rc = rawdev_unmount_all(); g_rawdev_ready = false; } - + return rc; } int nxfs_unmount_emummc() { int rc = 0; - + /* Unmount all emulated devices. */ if (g_emudev_ready) { rc = emudev_unmount_all(); g_emudev_ready = false; } - + return rc; } int nxfs_init() { int rc; - + /* Mount and register the SD card. */ rc = nxfs_mount_sd(); - + /* Set the SD card as the default file system device. */ if (rc == 0) { rc = fsdev_set_default_device("sdmc"); } - + return rc; } diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c index 8ca5fc162..5987dd0f1 100644 --- a/fusee/fusee-secondary/src/package2.c +++ b/fusee/fusee-secondary/src/package2.c @@ -90,13 +90,13 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm package2_patch_kernel(kernel, &kernel_size, is_sd_kernel, (void *)&orig_ini1, target_firmware); /* Ensure we know where embedded INI is if present, and we don't if not. */ - if ((target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 != NULL) || - (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 == NULL)) { + if ((target_firmware < ATMOSPHERE_TARGET_FIRMWARE_8_0_0 && orig_ini1 != NULL) || + (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0 && orig_ini1 == NULL)) { fatal_error("Error: inappropriate kernel embedded ini context"); } print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilding the INI1 section...\n"); - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_8_0_0) { package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1); } else { /* On 8.0.0, place INI1 right after kernelldr for our sanity. */ diff --git a/fusee/fusee-secondary/src/se.c b/fusee/fusee-secondary/src/se.c index 4f82ed568..86f416a02 100644 --- a/fusee/fusee-secondary/src/se.c +++ b/fusee/fusee-secondary/src/se.c @@ -13,7 +13,7 @@ * 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 <string.h> #include "utils.h" @@ -60,7 +60,7 @@ void se_verify_flags_cleared(void) { /* Set the flags for an AES keyslot. */ void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } @@ -79,7 +79,7 @@ void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { /* Set the flags for an RSA keyslot. */ void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_RSA_MAX) { generic_panic(); } @@ -98,7 +98,7 @@ void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) { void clear_aes_keyslot(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } @@ -112,7 +112,7 @@ void clear_aes_keyslot(unsigned int keyslot) { void clear_rsa_keyslot(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_RSA_MAX) { generic_panic(); } @@ -132,7 +132,7 @@ void clear_rsa_keyslot(unsigned int keyslot) { void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || key_size > KEYSIZE_AES_MAX) { generic_panic(); } @@ -145,7 +145,7 @@ void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) { void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_RSA_MAX || modulus_size > KEYSIZE_RSA_MAX || exp_size > KEYSIZE_RSA_MAX) { generic_panic(); } @@ -166,7 +166,7 @@ void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_ void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || iv_size > 0x10) { generic_panic(); } @@ -179,7 +179,7 @@ void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) { void clear_aes_keyslot_iv(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } @@ -198,7 +198,7 @@ void set_se_ctr(const void *ctr) { void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSLOT_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { generic_panic(); } @@ -235,7 +235,7 @@ void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, co void se_get_exp_mod_output(void *buf, size_t size) { size_t num_dwords = (size >> 2); - + if (num_dwords < 1) { return; } @@ -351,7 +351,7 @@ void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || ctr_size != 0x10) { generic_panic(); } @@ -382,7 +382,7 @@ void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const vo void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { generic_panic(); } @@ -403,7 +403,7 @@ void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_si void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { generic_panic(); } @@ -444,14 +444,24 @@ void aes_128_xts_nintendo_get_tweak(uint8_t *tweak, size_t sector) { } } -void aes_128_xts_nintendo_xor_with_tweak(unsigned int keyslot, size_t sector, uint8_t *dst, const uint8_t *src, size_t size) { +void aes_128_xts_nintendo_xor_with_tweak(unsigned int keyslot, size_t sector, uint8_t *dst, const uint8_t *src, size_t size, size_t crypto_sector_size) { if ((size & 0xF) || size == 0) { generic_panic(); } + + unsigned int sector_scale = crypto_sector_size / size; + unsigned int real_sector = sector / sector_scale; + uint8_t tweak[0x10]; - aes_128_xts_nintendo_get_tweak(tweak, sector); + aes_128_xts_nintendo_get_tweak(tweak, real_sector); se_aes_128_ecb_encrypt_block(keyslot, tweak, sizeof(tweak), tweak, sizeof(tweak)); - + + unsigned int num_pre_blocks = ((sector % sector_scale) * size) / 0x10; + + for (unsigned int pre = 0; pre < num_pre_blocks; pre++) { + shift_left_xor_rb_le(tweak); + } + for (unsigned int block = 0; block < (size >> 4); block++) { for (unsigned int i = 0; i < 0x10; i++) { dst[(block << 4) | i] = src[(block << 4) | i] ^ tweak[i]; @@ -460,16 +470,16 @@ void aes_128_xts_nintendo_xor_with_tweak(unsigned int keyslot, size_t sector, ui } } -void aes_128_xts_nintendo_crypt_sector(unsigned int keyslot_1, unsigned int keyslot_2, size_t sector, bool encrypt, void *dst, const void *src, size_t size) { +void aes_128_xts_nintendo_crypt_sector(unsigned int keyslot_1, unsigned int keyslot_2, size_t sector, bool encrypt, void *dst, const void *src, size_t size, size_t crypto_sector_size) { volatile tegra_se_t *se = se_get_regs(); - - if ((size & 0xF) || size == 0) { + + if ((size & 0xF) || size == 0 || crypto_sector_size < size || (crypto_sector_size % size) != 0) { generic_panic(); } - + /* XOR. */ - aes_128_xts_nintendo_xor_with_tweak(keyslot_2, sector, dst, src, size); - + aes_128_xts_nintendo_xor_with_tweak(keyslot_2, sector, dst, src, size, crypto_sector_size); + /* Encrypt/Decrypt. */ if (encrypt) { se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY); @@ -480,38 +490,39 @@ void aes_128_xts_nintendo_crypt_sector(unsigned int keyslot_1, unsigned int keys } se->SE_CRYPTO_LAST_BLOCK = (size >> 4) - 1; trigger_se_blocking_op(OP_START, dst, size, src, size); - + /* XOR. */ - aes_128_xts_nintendo_xor_with_tweak(keyslot_2, sector, dst, dst, size); + aes_128_xts_nintendo_xor_with_tweak(keyslot_2, sector, dst, dst, size, crypto_sector_size); } /* Encrypt with AES-XTS (Nintendo's custom tweak). */ -void se_aes_128_xts_nintendo_encrypt(unsigned int keyslot_1, unsigned int keyslot_2, size_t base_sector, void *dst, const void *src, size_t size, unsigned int sector_size) { - if ((size & 0xF) || size == 0) { +void se_aes_128_xts_nintendo_encrypt(unsigned int keyslot_1, unsigned int keyslot_2, size_t base_sector, void *dst, const void *src, size_t size, unsigned int sector_size, unsigned int crypto_sector_size) { + if ((size & 0xF) || size == 0 || crypto_sector_size < sector_size || (crypto_sector_size % sector_size) != 0) { generic_panic(); } size_t sector = base_sector; for (size_t ofs = 0; ofs < size; ofs += sector_size) { - aes_128_xts_nintendo_crypt_sector(keyslot_1, keyslot_2, sector, true, dst + ofs, src + ofs, sector_size); + aes_128_xts_nintendo_crypt_sector(keyslot_1, keyslot_2, sector, true, dst + ofs, src + ofs, sector_size, crypto_sector_size); sector++; } } /* Decrypt with AES-XTS (Nintendo's custom tweak). */ -void se_aes_128_xts_nintendo_decrypt(unsigned int keyslot_1, unsigned int keyslot_2, size_t base_sector, void *dst, const void *src, size_t size, unsigned int sector_size) { - if ((size & 0xF) || size == 0) { +void se_aes_128_xts_nintendo_decrypt(unsigned int keyslot_1, unsigned int keyslot_2, size_t base_sector, void *dst, const void *src, size_t size, unsigned int sector_size, unsigned int crypto_sector_size) { + if ((size & 0xF) || size == 0 || crypto_sector_size < sector_size || (crypto_sector_size % sector_size) != 0) { generic_panic(); } + size_t sector = base_sector; for (size_t ofs = 0; ofs < size; ofs += sector_size) { - aes_128_xts_nintendo_crypt_sector(keyslot_1, keyslot_2, sector, false, dst + ofs, src + ofs, sector_size); + aes_128_xts_nintendo_crypt_sector(keyslot_1, keyslot_2, sector, false, dst + ofs, src + ofs, sector_size, crypto_sector_size); sector++; } } void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } @@ -568,7 +579,7 @@ void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *iv) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || src_size < 0x10) { generic_panic(); } @@ -583,7 +594,7 @@ void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, co /* SHA256 Implementation. */ void se_calculate_sha256(void *dst, const void *src, size_t src_size) { volatile tegra_se_t *se = se_get_regs(); - + /* Setup config for SHA256, size = BITS(src_size) */ se->SE_CONFIG = (ENCMODE_SHA256 | ALG_SHA | DST_HASHREG); se->SE_SHA_CONFIG = 1; @@ -608,7 +619,7 @@ void se_calculate_sha256(void *dst, const void *src, size_t src_size) { /* RNG API */ void se_initialize_rng(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } @@ -628,7 +639,7 @@ void se_initialize_rng(unsigned int keyslot) { void se_generate_random(unsigned int keyslot, void *dst, size_t size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { generic_panic(); } diff --git a/fusee/fusee-secondary/src/se.h b/fusee/fusee-secondary/src/se.h index 4f01d26f9..9bcc33996 100644 --- a/fusee/fusee-secondary/src/se.h +++ b/fusee/fusee-secondary/src/se.h @@ -13,7 +13,7 @@ * 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 FUSEE_SE_H #define FUSEE_SE_H @@ -182,8 +182,8 @@ void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size); void set_se_ctr(const void *ctr); /* Secure AES API */ -void se_aes_128_xts_nintendo_decrypt(unsigned int keyslot_1, unsigned int keyslot_2, unsigned int base_sector, void *dst, const void *src, size_t size, unsigned int sector_size); -void se_aes_128_xts_nintendo_encrypt(unsigned int keyslot_1, unsigned int keyslot_2, unsigned int base_sector, void *dst, const void *src, size_t size, unsigned int sector_size); +void se_aes_128_xts_nintendo_decrypt(unsigned int keyslot_1, unsigned int keyslot_2, unsigned int base_sector, void *dst, const void *src, size_t size, unsigned int sector_size, unsigned int crypto_sector_size); +void se_aes_128_xts_nintendo_encrypt(unsigned int keyslot_1, unsigned int keyslot_2, unsigned int base_sector, void *dst, const void *src, size_t size, unsigned int sector_size, unsigned int crypto_sector_size); void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp index 73f497c4a..0eedb6d84 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp @@ -676,7 +676,7 @@ namespace ams::kern::arch::arm64::init { } ALWAYS_INLINE void InitializeFromState(uintptr_t state_val) { - if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { this->state = *reinterpret_cast<State *>(state_val); } else { this->state.next_address = state_val; diff --git a/libraries/libmesosphere/include/mesosphere/kern_common.hpp b/libraries/libmesosphere/include/mesosphere/kern_common.hpp index b9862e719..e27cfc96e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_common.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_common.hpp @@ -20,23 +20,7 @@ namespace ams::kern { constexpr size_t PageSize = 4_KB; - enum TargetFirmware : u32 { - TargetFirmware_1_0_0 = ATMOSPHERE_TARGET_FIRMWARE_100, - TargetFirmware_2_0_0 = ATMOSPHERE_TARGET_FIRMWARE_200, - TargetFirmware_3_0_0 = ATMOSPHERE_TARGET_FIRMWARE_300, - TargetFirmware_4_0_0 = ATMOSPHERE_TARGET_FIRMWARE_400, - TargetFirmware_5_0_0 = ATMOSPHERE_TARGET_FIRMWARE_500, - TargetFirmware_6_0_0 = ATMOSPHERE_TARGET_FIRMWARE_600, - TargetFirmware_6_2_0 = ATMOSPHERE_TARGET_FIRMWARE_620, - TargetFirmware_7_0_0 = ATMOSPHERE_TARGET_FIRMWARE_700, - TargetFirmware_8_0_0 = ATMOSPHERE_TARGET_FIRMWARE_800, - TargetFirmware_8_1_0 = ATMOSPHERE_TARGET_FIRMWARE_810, - TargetFirmware_9_0_0 = ATMOSPHERE_TARGET_FIRMWARE_900, - TargetFirmware_9_1_0 = ATMOSPHERE_TARGET_FIRMWARE_910, - TargetFirmware_10_0_0 = ATMOSPHERE_TARGET_FIRMWARE_1000, - }; - - TargetFirmware GetTargetFirmware(); + ams::TargetFirmware GetTargetFirmware(); } diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp index 40ac14db3..73b69fba7 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp @@ -21,34 +21,18 @@ namespace ams::exosphere { - #define AMS_DEFINE_TARGET_FIRMWARE_ENUM(n) TargetFirmware_##n = ATMOSPHERE_TARGET_FIRMWARE_##n - enum TargetFirmware : u32 { - AMS_DEFINE_TARGET_FIRMWARE_ENUM(100), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(200), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(300), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(400), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(500), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(600), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(620), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(700), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(800), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(810), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(900), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(910), - AMS_DEFINE_TARGET_FIRMWARE_ENUM(1000), - }; - #undef AMS_DEFINE_TARGET_FIRMWARE_ENUM + using TargetFirmware = ams::TargetFirmware; constexpr ALWAYS_INLINE u32 GetVersion(u32 major, u32 minor, u32 micro) { return (major << 16) | (minor << 8) | (micro); } struct ApiInfo { - using MasterKeyRevision = util::BitPack64::Field<0, 8, u32>; - using TargetFirmwareVersion = util::BitPack64::Field<MasterKeyRevision::Next, 8, TargetFirmware>; - using MicroVersion = util::BitPack64::Field<TargetFirmwareVersion::Next, 8, u32>; - using MinorVersion = util::BitPack64::Field<MicroVersion::Next, 8, u32>; - using MajorVersion = util::BitPack64::Field<MinorVersion::Next, 8, u32>; + using TargetFirmwareVersion = util::BitPack64::Field<0, 32, TargetFirmware>; + using MasterKeyRevision = util::BitPack64::Field<TargetFirmwareVersion::Next, 8, u32>; + using MicroVersion = util::BitPack64::Field<MasterKeyRevision::Next, 8, u32>; + using MinorVersion = util::BitPack64::Field<MicroVersion::Next, 8, u32>; + using MajorVersion = util::BitPack64::Field<MinorVersion::Next, 8, u32>; util::BitPack64 value; diff --git a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp index 299828a8a..b93c6e738 100644 --- a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp @@ -19,22 +19,47 @@ namespace ams::hos { - enum Version : u16 { - Version_Min = 0, - Version_1_0_0 = Version_Min, - Version_2_0_0 = 1, - Version_3_0_0 = 2, - Version_4_0_0 = 3, - Version_5_0_0 = 4, - Version_6_0_0 = 5, - Version_7_0_0 = 6, - Version_8_0_0 = 7, - Version_8_1_0 = 8, - Version_9_0_0 = 9, - Version_9_1_0 = 10, - Version_10_0_0 = 11, - Version_Current = Version_10_0_0, - Version_Max = 32, + enum Version : u32 { + Version_Min = ::ams::TargetFirmware_Min, + + Version_1_0_0 = ::ams::TargetFirmware_1_0_0, + Version_2_0_0 = ::ams::TargetFirmware_2_0_0, + Version_2_1_0 = ::ams::TargetFirmware_2_1_0, + Version_2_2_0 = ::ams::TargetFirmware_2_2_0, + Version_2_3_0 = ::ams::TargetFirmware_2_3_0, + Version_3_0_0 = ::ams::TargetFirmware_3_0_0, + Version_3_0_1 = ::ams::TargetFirmware_3_0_1, + Version_3_0_2 = ::ams::TargetFirmware_3_0_2, + Version_4_0_0 = ::ams::TargetFirmware_4_0_0, + Version_4_0_1 = ::ams::TargetFirmware_4_0_1, + Version_4_1_0 = ::ams::TargetFirmware_4_1_0, + Version_5_0_0 = ::ams::TargetFirmware_5_0_0, + Version_5_0_1 = ::ams::TargetFirmware_5_0_1, + Version_5_0_2 = ::ams::TargetFirmware_5_0_2, + Version_5_1_0 = ::ams::TargetFirmware_5_1_0, + Version_6_0_0 = ::ams::TargetFirmware_6_0_0, + Version_6_0_1 = ::ams::TargetFirmware_6_0_1, + Version_6_1_0 = ::ams::TargetFirmware_6_1_0, + Version_6_2_0 = ::ams::TargetFirmware_6_2_0, + Version_7_0_0 = ::ams::TargetFirmware_7_0_0, + Version_7_0_1 = ::ams::TargetFirmware_7_0_1, + Version_8_0_0 = ::ams::TargetFirmware_8_0_0, + Version_8_0_1 = ::ams::TargetFirmware_8_0_1, + Version_8_1_0 = ::ams::TargetFirmware_8_1_0, + Version_8_1_1 = ::ams::TargetFirmware_8_1_1, + Version_9_0_0 = ::ams::TargetFirmware_9_0_0, + Version_9_0_1 = ::ams::TargetFirmware_9_0_1, + Version_9_1_0 = ::ams::TargetFirmware_9_1_0, + Version_9_2_0 = ::ams::TargetFirmware_9_2_0, + Version_10_0_0 = ::ams::TargetFirmware_10_0_0, + Version_10_0_1 = ::ams::TargetFirmware_10_0_1, + Version_10_0_2 = ::ams::TargetFirmware_10_0_2, + + Version_Current = ::ams::TargetFirmware_Current, + + Version_Max = ::ams::TargetFirmware_Max, }; + + } diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp index 47ac08c21..44e35b4f7 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp @@ -61,7 +61,7 @@ namespace ams::sf::cmif { return this->handler; } }; - static_assert(std::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x10, "sizeof(ServiceCommandMeta)"); + static_assert(std::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)"); namespace impl { diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index b11d56c83..56f4b65b8 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -35,46 +35,9 @@ namespace ams::hos { return; } - switch (exosphere::GetApiInfo().GetTargetFirmware()) { - case exosphere::TargetFirmware_100: - g_hos_version = hos::Version_1_0_0; - break; - case exosphere::TargetFirmware_200: - g_hos_version = hos::Version_2_0_0; - break; - case exosphere::TargetFirmware_300: - g_hos_version = hos::Version_3_0_0; - break; - case exosphere::TargetFirmware_400: - g_hos_version = hos::Version_4_0_0; - break; - case exosphere::TargetFirmware_500: - g_hos_version = hos::Version_5_0_0; - break; - case exosphere::TargetFirmware_600: - case exosphere::TargetFirmware_620: - g_hos_version = hos::Version_6_0_0; - break; - case exosphere::TargetFirmware_700: - g_hos_version = hos::Version_7_0_0; - break; - case exosphere::TargetFirmware_800: - g_hos_version = hos::Version_8_0_0; - break; - case exosphere::TargetFirmware_810: - g_hos_version = hos::Version_8_1_0; - break; - case exosphere::TargetFirmware_900: - g_hos_version = hos::Version_9_0_0; - break; - case exosphere::TargetFirmware_910: - g_hos_version = hos::Version_9_1_0; - break; - case exosphere::TargetFirmware_1000: - g_hos_version = hos::Version_10_0_0; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } + /* Hos version is a direct copy of target firmware, just renamed. */ + g_hos_version = static_cast<hos::Version>(exosphere::GetApiInfo().GetTargetFirmware()); + AMS_ABORT_UNLESS(g_hos_version <= hos::Version_Max); __atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST); } @@ -87,69 +50,10 @@ namespace ams::hos { } void SetVersionForLibnxInternal() { - u32 major = 0, minor = 0, micro = 0; - switch (hos::GetVersion()) { - case hos::Version_1_0_0: - major = 1; - minor = 0; - micro = 0; - break; - case hos::Version_2_0_0: - major = 2; - minor = 0; - micro = 0; - break; - case hos::Version_3_0_0: - major = 3; - minor = 0; - micro = 0; - break; - case hos::Version_4_0_0: - major = 4; - minor = 0; - micro = 0; - break; - case hos::Version_5_0_0: - major = 5; - minor = 0; - micro = 0; - break; - case hos::Version_6_0_0: - major = 6; - minor = 0; - micro = 0; - break; - case hos::Version_7_0_0: - major = 7; - minor = 0; - micro = 0; - break; - case hos::Version_8_0_0: - major = 8; - minor = 0; - micro = 0; - break; - case hos::Version_8_1_0: - major = 8; - minor = 1; - micro = 0; - break; - case hos::Version_9_0_0: - major = 9; - minor = 0; - micro = 0; - case hos::Version_9_1_0: - major = 9; - minor = 1; - micro = 0; - break; - case hos::Version_10_0_0: - major = 10; - minor = 0; - micro = 0; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } + const u32 hos_version_val = static_cast<u32>(hos::GetVersion()); + const u32 major = (hos_version_val >> 24) & 0xFF; + const u32 minor = (hos_version_val >> 16) & 0xFF; + const u32 micro = (hos_version_val >> 8) & 0xFF; hosversionSet(MAKEHOSVERSION(major, minor, micro)); } diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index 25764523f..1d51bbe61 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -15,22 +15,94 @@ */ #pragma once -#define ATMOSPHERE_TARGET_FIRMWARE_100 1 -#define ATMOSPHERE_TARGET_FIRMWARE_200 2 -#define ATMOSPHERE_TARGET_FIRMWARE_300 3 -#define ATMOSPHERE_TARGET_FIRMWARE_400 4 -#define ATMOSPHERE_TARGET_FIRMWARE_500 5 -#define ATMOSPHERE_TARGET_FIRMWARE_600 6 -#define ATMOSPHERE_TARGET_FIRMWARE_620 7 -#define ATMOSPHERE_TARGET_FIRMWARE_700 8 -#define ATMOSPHERE_TARGET_FIRMWARE_800 9 -#define ATMOSPHERE_TARGET_FIRMWARE_810 10 -#define ATMOSPHERE_TARGET_FIRMWARE_900 11 -#define ATMOSPHERE_TARGET_FIRMWARE_910 12 -#define ATMOSPHERE_TARGET_FIRMWARE_1000 13 +#define ATMOSPHERE_TARGET_FIRMWARE_WITH_REVISION(major, minor, micro, rev) ((major << 24) | (minor << 16) | (micro << 8) | (rev)) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_1000 +#define ATMOSPHERE_TARGET_FIRMWARE(major, minor, micro) ATMOSPHERE_TARGET_FIRMWARE_WITH_REVISION(major, minor, micro, 0) -#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100 +#define ATMOSPHERE_TARGET_FIRMWARE_1_0_0 ATMOSPHERE_TARGET_FIRMWARE( 1, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_2_0_0 ATMOSPHERE_TARGET_FIRMWARE( 2, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_2_1_0 ATMOSPHERE_TARGET_FIRMWARE( 2, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_2_2_0 ATMOSPHERE_TARGET_FIRMWARE( 2, 2, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_2_3_0 ATMOSPHERE_TARGET_FIRMWARE( 2, 3, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_3_0_0 ATMOSPHERE_TARGET_FIRMWARE( 3, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_3_0_1 ATMOSPHERE_TARGET_FIRMWARE( 3, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_3_0_2 ATMOSPHERE_TARGET_FIRMWARE( 3, 0, 2) +#define ATMOSPHERE_TARGET_FIRMWARE_4_0_0 ATMOSPHERE_TARGET_FIRMWARE( 4, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_4_0_1 ATMOSPHERE_TARGET_FIRMWARE( 4, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_4_1_0 ATMOSPHERE_TARGET_FIRMWARE( 4, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_5_0_0 ATMOSPHERE_TARGET_FIRMWARE( 5, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_5_0_1 ATMOSPHERE_TARGET_FIRMWARE( 5, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_5_0_2 ATMOSPHERE_TARGET_FIRMWARE( 5, 0, 2) +#define ATMOSPHERE_TARGET_FIRMWARE_5_1_0 ATMOSPHERE_TARGET_FIRMWARE( 5, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_6_0_0 ATMOSPHERE_TARGET_FIRMWARE( 6, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_6_0_1 ATMOSPHERE_TARGET_FIRMWARE( 6, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_6_1_0 ATMOSPHERE_TARGET_FIRMWARE( 6, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_6_2_0 ATMOSPHERE_TARGET_FIRMWARE( 6, 2, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_7_0_0 ATMOSPHERE_TARGET_FIRMWARE( 7, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_7_0_1 ATMOSPHERE_TARGET_FIRMWARE( 7, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_8_0_0 ATMOSPHERE_TARGET_FIRMWARE( 8, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_8_0_1 ATMOSPHERE_TARGET_FIRMWARE( 8, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_8_1_0 ATMOSPHERE_TARGET_FIRMWARE( 8, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_8_1_1 ATMOSPHERE_TARGET_FIRMWARE( 8, 1, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_9_0_0 ATMOSPHERE_TARGET_FIRMWARE( 9, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_9_0_1 ATMOSPHERE_TARGET_FIRMWARE( 9, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_9_1_0 ATMOSPHERE_TARGET_FIRMWARE( 9, 1, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_9_2_0 ATMOSPHERE_TARGET_FIRMWARE( 9, 2, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_10_0_0 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_10_0_1 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_10_0_2 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 2) + +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_0_2 + +#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT -#define ATMOSPHERE_TARGET_FIRMWARE_COUNT ((ATMOSPHERE_TARGET_FIRMWARE_MAX - ATMOSPHERE_TARGET_FIRMWARE_MIN) + 1) \ No newline at end of file + +#ifdef __cplusplus + +namespace ams { + + enum TargetFirmware : u32 { + TargetFirmware_Min = ATMOSPHERE_TARGET_FIRMWARE_MIN, + + TargetFirmware_1_0_0 = ATMOSPHERE_TARGET_FIRMWARE_1_0_0, + TargetFirmware_2_0_0 = ATMOSPHERE_TARGET_FIRMWARE_2_0_0, + TargetFirmware_2_1_0 = ATMOSPHERE_TARGET_FIRMWARE_2_1_0, + TargetFirmware_2_2_0 = ATMOSPHERE_TARGET_FIRMWARE_2_2_0, + TargetFirmware_2_3_0 = ATMOSPHERE_TARGET_FIRMWARE_2_3_0, + TargetFirmware_3_0_0 = ATMOSPHERE_TARGET_FIRMWARE_3_0_0, + TargetFirmware_3_0_1 = ATMOSPHERE_TARGET_FIRMWARE_3_0_1, + TargetFirmware_3_0_2 = ATMOSPHERE_TARGET_FIRMWARE_3_0_2, + TargetFirmware_4_0_0 = ATMOSPHERE_TARGET_FIRMWARE_4_0_0, + TargetFirmware_4_0_1 = ATMOSPHERE_TARGET_FIRMWARE_4_0_1, + TargetFirmware_4_1_0 = ATMOSPHERE_TARGET_FIRMWARE_4_1_0, + TargetFirmware_5_0_0 = ATMOSPHERE_TARGET_FIRMWARE_5_0_0, + TargetFirmware_5_0_1 = ATMOSPHERE_TARGET_FIRMWARE_5_0_1, + TargetFirmware_5_0_2 = ATMOSPHERE_TARGET_FIRMWARE_5_0_2, + TargetFirmware_5_1_0 = ATMOSPHERE_TARGET_FIRMWARE_5_1_0, + TargetFirmware_6_0_0 = ATMOSPHERE_TARGET_FIRMWARE_6_0_0, + TargetFirmware_6_0_1 = ATMOSPHERE_TARGET_FIRMWARE_6_0_1, + TargetFirmware_6_1_0 = ATMOSPHERE_TARGET_FIRMWARE_6_1_0, + TargetFirmware_6_2_0 = ATMOSPHERE_TARGET_FIRMWARE_6_2_0, + TargetFirmware_7_0_0 = ATMOSPHERE_TARGET_FIRMWARE_7_0_0, + TargetFirmware_7_0_1 = ATMOSPHERE_TARGET_FIRMWARE_7_0_1, + TargetFirmware_8_0_0 = ATMOSPHERE_TARGET_FIRMWARE_8_0_0, + TargetFirmware_8_0_1 = ATMOSPHERE_TARGET_FIRMWARE_8_0_1, + TargetFirmware_8_1_0 = ATMOSPHERE_TARGET_FIRMWARE_8_1_0, + TargetFirmware_8_1_1 = ATMOSPHERE_TARGET_FIRMWARE_8_1_1, + TargetFirmware_9_0_0 = ATMOSPHERE_TARGET_FIRMWARE_9_0_0, + TargetFirmware_9_0_1 = ATMOSPHERE_TARGET_FIRMWARE_9_0_1, + TargetFirmware_9_1_0 = ATMOSPHERE_TARGET_FIRMWARE_9_1_0, + TargetFirmware_9_2_0 = ATMOSPHERE_TARGET_FIRMWARE_9_2_0, + TargetFirmware_10_0_0 = ATMOSPHERE_TARGET_FIRMWARE_10_0_0, + TargetFirmware_10_0_1 = ATMOSPHERE_TARGET_FIRMWARE_10_0_1, + TargetFirmware_10_0_2 = ATMOSPHERE_TARGET_FIRMWARE_10_0_2, + + TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT, + + TargetFirmware_Max = ATMOSPHERE_TARGET_FIRMWARE_MAX, + }; + static_assert(TargetFirmware_Current <= TargetFirmware_Max); + +} + +#endif diff --git a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp index aa9df8c63..c13beb815 100644 --- a/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp +++ b/mesosphere/kernel/source/arch/arm64/init/kern_init_core.cpp @@ -50,7 +50,7 @@ namespace ams::kern::init { static_assert(KernelResourceRegionSize + ExtraKernelResourceSize > InitialProcessBinarySizeMax); /* 10.0.0 reduced the kernel resource region size by 64K. */ - if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { resource_region_size -= KernelResourceReduction_10_0_0; } return resource_region_size; diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index 45a80d1d9..cb36fab23 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -48,7 +48,7 @@ namespace ams::kern::init::loader { static_assert(KernelResourceRegionSize + ExtraKernelResourceSize > InitialProcessBinarySizeMax); /* 10.0.0 reduced the kernel resource region size by 64K. */ - if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { resource_region_size -= KernelResourceReduction_10_0_0; } return resource_region_size; @@ -118,7 +118,7 @@ namespace ams::kern::init::loader { cpu::TranslationControlRegisterAccessor(TcrValue).Store(); /* Perform cpu-specific setup on < 10.0.0. */ - if (kern::GetTargetFirmware() < kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) { SavedRegisterState saved_registers; SaveRegistersToTpidrEl1(&saved_registers); ON_SCOPE_EXIT { VerifyAndClearTpidrEl1(&saved_registers); }; @@ -305,7 +305,7 @@ namespace ams::kern::init::loader { ttbr1_table.Map(virtual_base_address + rw_offset, bss_end_offset - rw_offset, base_address + rw_offset, KernelRwDataAttribute, g_initial_page_allocator); /* On 10.0.0+, Physically randomize the kernel region. */ - if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { ttbr1_table.PhysicallyRandomize(virtual_base_address + rx_offset, bss_end_offset - rx_offset, true); cpu::StoreEntireCacheForInit(); } @@ -333,7 +333,7 @@ namespace ams::kern::init::loader { uintptr_t GetFinalPageAllocatorState() { g_initial_page_allocator.GetFinalState(std::addressof(g_final_page_allocator_state)); - if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) { + if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { return reinterpret_cast<uintptr_t>(std::addressof(g_final_page_allocator_state)); } else { return g_final_page_allocator_state.next_address; diff --git a/sept/sept-primary/src/fuse.c b/sept/sept-primary/src/fuse.c index 0e94817bf..3a1eecd99 100644 --- a/sept/sept-primary/src/fuse.c +++ b/sept/sept-primary/src/fuse.c @@ -204,7 +204,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -213,7 +213,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; diff --git a/sept/sept-secondary/src/fuse.c b/sept/sept-secondary/src/fuse.c index 1062b1d81..bd8d9dac2 100644 --- a/sept/sept-secondary/src/fuse.c +++ b/sept/sept-secondary/src/fuse.c @@ -206,7 +206,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) { + if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); if (hardware_type >= 1) { return (hardware_type > 2) ? 3 : hardware_type - 1; @@ -215,7 +215,7 @@ uint32_t fuse_get_hardware_type(uint32_t target_firmware) { } else { return 3; } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) { /* Firmware versions from 4.0.0 to 6.2.0. */ + } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ static const uint32_t types[] = {0,1,4,3}; hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type--; From 0e289461bb6b9a0baf3e1ff680557e531326e38f Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 6 May 2020 22:34:34 -0700 Subject: [PATCH 023/118] ams: use psc acknowledge_ex on 5.1.0+ (fixes wake-from-sleep on 5.1.0) --- .../include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp | 2 +- .../libstratosphere/source/psc/psc_pm_module.os.horizon.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp index bf2e86a7a..5673bd4ab 100644 --- a/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp +++ b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp @@ -43,7 +43,7 @@ namespace ams::psc::sf { MAKE_SERVICE_COMMAND_META(GetRequest), MAKE_SERVICE_COMMAND_META(Acknowledge), MAKE_SERVICE_COMMAND_META(Finalize), - MAKE_SERVICE_COMMAND_META(AcknowledgeEx, hos::Version_6_0_0), /* TODO: This is really 5.1.0... */ + MAKE_SERVICE_COMMAND_META(AcknowledgeEx, hos::Version_5_1_0), }; }; diff --git a/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp index 22d947a51..146e5e47a 100644 --- a/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp +++ b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp @@ -60,7 +60,7 @@ namespace ams::psc { R_ABORT_UNLESS(res); R_UNLESS(this->initialized, psc::ResultNotInitialized()); - if (hos::GetVersion() >= hos::Version_6_0_0) { + if (hos::GetVersion() >= hos::Version_5_1_0) { return this->intf->AcknowledgeEx(state); } else { return this->intf->Acknowledge(); From 17b6bcfd37be626d95f2a18fe6d1ea142f336a76 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 6 May 2020 22:39:11 -0700 Subject: [PATCH 024/118] loader: correct anti-downgrade tables for new version scheme --- .../loader/source/ldr_process_creation.cpp | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index eb68c03f9..e715e3b3f 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -94,27 +94,20 @@ namespace ams::ldr { #ifdef LDR_VALIDATE_PROCESS_VERSION const MinimumProgramVersion *entries = nullptr; size_t num_entries = 0; - switch (hos::GetVersion()) { - case hos::Version_8_1_0: - entries = g_MinimumProgramVersions810; - num_entries = g_MinimumProgramVersionsCount810; - break; - case hos::Version_9_0_0: - entries = g_MinimumProgramVersions900; - num_entries = g_MinimumProgramVersionsCount900; - break; - case hos::Version_9_1_0: - entries = g_MinimumProgramVersions910; - num_entries = g_MinimumProgramVersionsCount910; - break; - case hos::Version_10_0_0: - entries = g_MinimumProgramVersions1000; - num_entries = g_MinimumProgramVersionsCount1000; - break; - default: - entries = nullptr; - num_entries = 0; - break; + + const auto hos_version = hos::GetVersion(); + if (hos_version >= hos::Version_10_0_0) { + entries = g_MinimumProgramVersions1000; + num_entries = g_MinimumProgramVersionsCount1000; + } else if (hos_version >= hos::Version_9_1_0) { + entries = g_MinimumProgramVersions910; + num_entries = g_MinimumProgramVersionsCount910; + } else if (hos_version >= hos::Version_9_0_0) { + entries = g_MinimumProgramVersions900; + num_entries = g_MinimumProgramVersionsCount900; + } else if (hos_version >= hos::Version_8_1_0) { + entries = g_MinimumProgramVersions810; + num_entries = g_MinimumProgramVersionsCount810; } for (size_t i = 0; i < num_entries; i++) { From 3a1ccdd9198939152017e56bfe77f2ad7d4e3360 Mon Sep 17 00:00:00 2001 From: SciresM <Sciresm@gmail.com> Date: Mon, 11 May 2020 15:02:10 -0700 Subject: [PATCH 025/118] Switch atmosphere's build target to C++20. (#952) * ams: update to build with gcc10/c++20 * remove mno-outline-atomics * ams: take care of most TODO C++20s * fusee/sept: update for gcc10 * whoosh, your code now uses pre-compiled headers * make: dependency fixes --- exosphere/Makefile | 2 + exosphere/src/mmu.h | 4 +- exosphere/src/my_libc.c | 24 +-- fusee/fusee-primary/Makefile | 2 +- fusee/fusee-primary/src/fs_utils.c | 21 +- fusee/fusee-primary/src/fs_utils.h | 6 +- fusee/fusee-primary/src/lib/ini.c | 3 + fusee/fusee-secondary/src/lib/ini.c | 3 + fusee/fusee-secondary/src/nxboot.c | 12 ++ libraries/config/common.mk | 2 +- libraries/libmesosphere/Makefile | 5 +- .../arm64/kern_k_interrupt_controller.hpp | 8 +- .../mesosphere/init/kern_init_layout.hpp | 2 +- .../include/mesosphere/kern_k_auto_object.hpp | 5 +- .../mesosphere/kern_k_capabilities.hpp | 4 +- .../mesosphere/kern_k_current_context.hpp | 4 +- .../mesosphere/kern_k_page_table_base.hpp | 2 +- .../mesosphere/kern_k_priority_queue.hpp | 51 ++--- .../include/mesosphere/kern_k_scheduler.hpp | 11 +- .../mesosphere/kern_k_scheduler_lock.hpp | 19 +- .../include/mesosphere/kern_k_scoped_lock.hpp | 17 +- .../svc/kern_svc_k_user_pointer.hpp | 85 ++++---- .../source/kern_k_address_space_info.cpp | 26 +-- .../source/kern_k_object_name.cpp | 5 +- libraries/libstratosphere/Makefile | 7 +- .../include/stratosphere/ams/ams_types.hpp | 2 +- .../capsrv_screen_shot_decode_option.hpp | 2 +- .../include/stratosphere/cfg/cfg_types.hpp | 2 +- .../stratosphere/dmnt/dmnt_cheat_types.hpp | 6 +- .../include/stratosphere/erpt/erpt_types.hpp | 4 +- .../stratosphere/fatal/fatal_types.hpp | 124 +++++------ .../include/stratosphere/fs/fs_common.hpp | 2 +- .../fs/fs_dbm_hierarchical_rom_file_table.hpp | 10 +- .../fs/fs_dbm_rom_key_value_storage.hpp | 4 +- .../stratosphere/fs/fs_dbm_rom_path_tool.hpp | 2 +- .../stratosphere/fs/fs_dbm_rom_types.hpp | 6 +- .../include/stratosphere/fs/fs_file.hpp | 4 +- .../stratosphere/fs/fs_memory_management.hpp | 2 +- .../stratosphere/fs/fs_query_range.hpp | 2 +- .../include/stratosphere/fs/fs_rights_id.hpp | 2 +- .../stratosphere/fs/fs_save_data_types.hpp | 6 +- .../stratosphere/fssrv/fssrv_sf_path.hpp | 2 +- .../fssystem_file_system_buddy_heap.hpp | 2 +- ...system_dbm_hierarchical_rom_file_table.hpp | 10 +- .../fssystem_dbm_rom_key_value_storage.hpp | 4 +- .../fssystem/fssystem_dbm_rom_path_tool.hpp | 2 +- .../fssystem/fssystem_dbm_rom_types.hpp | 6 +- .../fssystem_partition_file_system_meta.hpp | 4 +- .../kvdb/kvdb_file_key_value_cache.hpp | 2 +- .../kvdb/kvdb_file_key_value_store.hpp | 10 +- .../kvdb/kvdb_memory_key_value_store.hpp | 10 +- .../include/stratosphere/ldr/ldr_types.hpp | 12 +- .../include/stratosphere/lr/lr_types.hpp | 2 +- .../stratosphere/mem/impl/mem_impl_common.hpp | 2 +- .../stratosphere/ncm/ncm_content_info.hpp | 2 +- .../ncm/ncm_content_manager_impl.hpp | 2 +- .../stratosphere/ncm/ncm_program_location.hpp | 2 +- .../stratosphere/ncm/ncm_rights_id.hpp | 2 +- .../stratosphere/os/os_rw_lock_types.hpp | 2 +- .../include/stratosphere/pgl/pgl_types.hpp | 2 +- .../include/stratosphere/pm/pm_types.hpp | 2 +- .../include/stratosphere/reg.hpp | 6 +- .../factory/settings_device_certificate.hpp | 6 +- .../factory/settings_serial_number.hpp | 2 +- .../settings/settings_fwdbg_types.hpp | 4 +- .../stratosphere/settings/settings_types.hpp | 4 +- .../cmif/sf_cmif_server_message_processor.hpp | 2 +- .../sf/cmif/sf_cmif_service_dispatch.hpp | 2 +- .../sf/impl/sf_impl_command_serialization.hpp | 10 +- .../include/stratosphere/sf/sf_out.hpp | 4 +- .../source/ams/ams_emummc_api.cpp | 1 - .../source/ams/ams_environment.cpp | 4 +- .../source/ams/ams_exosphere_api.cpp | 1 - .../decodersrv_decoder_work_memory.hpp | 2 +- .../libstratosphere/source/cfg/cfg_flags.cpp | 1 - .../source/cfg/cfg_override.cpp | 1 - .../source/cfg/cfg_privileged_process.cpp | 1 - .../source/cfg/cfg_sd_card.cpp | 1 - .../source/fs/fs_file_path_hash.hpp | 2 +- .../source/fs/fsa/fs_mount_name.hpp | 2 +- .../fssystem_partition_file_system_meta.cpp | 2 +- .../fssystem/fssystem_romfs_filesystem.cpp | 10 +- .../source/kvdb/kvdb_archive.cpp | 5 +- .../source/kvdb/kvdb_file_key_value_store.cpp | 1 - .../libstratosphere/source/map/map_api.cpp | 1 - .../heap/mem_impl_heap_tls_heap_static.hpp | 8 +- .../ncm/ncm_content_meta_database_impl.cpp | 2 +- .../source/os/impl/os_resource_manager.cpp | 3 +- .../os/impl/os_waitable_manager_impl.cpp | 1 + .../libstratosphere/source/os/os_event.cpp | 1 + .../source/os/os_interrupt_event.cpp | 1 + .../source/os/os_message_queue.cpp | 1 + .../source/os/os_semaphore.cpp | 1 + .../source/pgl/srv/pgl_srv_shell.cpp | 2 +- .../libstratosphere/source/sm/sm_api.cpp | 1 + .../source/sm/sm_manager_api.cpp | 1 + .../libstratosphere/source/sm/sm_mitm_api.cpp | 1 + .../libstratosphere/source/sm/sm_utils.cpp | 1 + .../source/updater/updater_api.cpp | 2 - .../source/updater/updater_bis_management.cpp | 1 + .../source/updater/updater_bis_save.cpp | 1 + .../source/updater/updater_files.cpp | 1 + .../source/updater/updater_paths.cpp | 1 + libraries/libstratosphere/source/util/ini.c | 3 + .../crypto/crypto_rsa_oaep_decryptor.hpp | 2 +- .../crypto/crypto_rsa_oaep_encryptor.hpp | 2 +- .../crypto/crypto_rsa_pss_verifier.hpp | 2 +- .../crypto/impl/crypto_hash_function.hpp | 18 +- .../crypto/impl/crypto_rsa_oaep_impl.hpp | 2 +- .../crypto/impl/crypto_rsa_pss_impl.hpp | 2 +- .../crypto/impl/crypto_sha256_impl.hpp | 2 +- .../libvapours/include/vapours/defines.hpp | 3 - .../libvapours/include/vapours/includes.hpp | 3 + libraries/libvapours/include/vapours/span.hpp | 202 +----------------- .../impl/svc_codegen_impl_code_generator.hpp | 7 +- .../codegen/impl/svc_codegen_impl_common.hpp | 2 +- .../svc_codegen_impl_kernel_svc_wrapper.hpp | 93 ++++---- .../codegen/impl/svc_codegen_impl_layout.hpp | 12 +- .../svc_codegen_impl_layout_conversion.hpp | 22 +- .../impl/svc_codegen_impl_meta_code.hpp | 19 +- .../impl/svc_codegen_impl_parameter.hpp | 6 +- .../vapours/svc/ipc/svc_message_buffer.hpp | 26 +-- libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_bitpack.hpp | 10 +- .../include/vapours/util/util_bitutil.hpp | 42 ++-- .../include/vapours/util/util_endian.hpp | 30 +-- .../include/vapours/util/util_fourcc.hpp | 15 +- .../include/vapours/util/util_type_traits.hpp | 26 +++ .../source/kern_kernel_instantiations.cpp | 26 +-- sept/sept-secondary/src/fs_utils.c | 20 +- sept/sept-secondary/src/fs_utils.h | 6 +- sept/sept-secondary/src/lib/ini.c | 3 + stratosphere/ams_mitm/source/amsmitm_main.cpp | 1 + .../ams_mitm/source/amsmitm_module.hpp | 26 ++- .../source/amsmitm_module_management.cpp | 2 +- .../source/bpc_mitm/bpc_ams_module.cpp | 1 + .../source/bpc_mitm/bpc_ams_power_utils.cpp | 1 + .../source/bpc_mitm/bpc_ams_service.cpp | 1 + .../source/bpc_mitm/bpc_mitm_service.cpp | 1 + .../source/bpc_mitm/bpcmitm_module.cpp | 1 + .../source/fs_mitm/fs_mitm_service.cpp | 1 + .../source/fs_mitm/fsmitm_boot0storage.cpp | 1 + .../ams_mitm/source/fs_mitm/fsmitm_module.cpp | 1 + .../ams_mitm/source/fs_mitm/fsmitm_romfs.cpp | 6 +- .../source/fs_mitm/fsmitm_save_utils.cpp | 1 + .../source/hid_mitm/hid_mitm_service.cpp | 1 + .../source/hid_mitm/hidmitm_module.cpp | 1 + .../ams_mitm/source/ns_mitm/nsmitm_module.cpp | 1 + .../source/set_mitm/set_mitm_service.cpp | 1 + .../source/set_mitm/setmitm_module.cpp | 1 + .../source/set_mitm/setsys_mitm_service.cpp | 1 + .../source/set_mitm/settings_sd_kvs.cpp | 3 +- stratosphere/boot/Makefile | 2 + .../boot/source/boot_battery_driver.cpp | 1 + .../boot/source/boot_battery_icons.cpp | 1 + stratosphere/boot/source/boot_boot_reason.cpp | 1 + stratosphere/boot/source/boot_calibration.cpp | 1 + .../boot/source/boot_change_voltage.cpp | 1 + .../boot/source/boot_charger_driver.cpp | 1 + .../boot/source/boot_check_battery.cpp | 1 + stratosphere/boot/source/boot_check_clock.cpp | 1 + .../boot_clock_initial_configuration.cpp | 1 + stratosphere/boot/source/boot_display.cpp | 1 + stratosphere/boot/source/boot_fan_enable.cpp | 1 + stratosphere/boot/source/boot_i2c_utils.cpp | 1 + stratosphere/boot/source/boot_main.cpp | 1 + stratosphere/boot/source/boot_pcv.cpp | 1 + stratosphere/boot/source/boot_pmc_wrapper.cpp | 1 + stratosphere/boot/source/boot_pmic_driver.cpp | 1 + stratosphere/boot/source/boot_power_utils.cpp | 1 + .../boot/source/boot_repair_boot_images.cpp | 1 + stratosphere/boot/source/boot_rtc_driver.cpp | 1 + .../boot/source/boot_splash_screen.cpp | 1 + stratosphere/boot/source/boot_wake_pins.cpp | 1 + .../gpio/gpio_initial_configuration.cpp | 4 +- stratosphere/boot/source/gpio/gpio_utils.cpp | 4 +- .../boot/source/i2c/driver/i2c_api.cpp | 1 + .../i2c/driver/impl/i2c_bus_accessor.cpp | 1 + .../i2c/driver/impl/i2c_device_config.cpp | 1 + .../i2c/driver/impl/i2c_resource_manager.cpp | 1 + .../source/i2c/driver/impl/i2c_session.cpp | 1 + .../boot/source/i2c/i2c_command_list.cpp | 1 + .../pinmux/pinmux_initial_configuration.cpp | 1 + .../boot/source/pinmux/pinmux_utils.cpp | 1 + stratosphere/boot2/source/boot2_main.cpp | 2 + .../creport/source/creport_crash_report.cpp | 5 +- stratosphere/creport/source/creport_main.cpp | 1 + .../creport/source/creport_modules.cpp | 1 + .../creport/source/creport_scoped_file.cpp | 1 + .../creport/source/creport_threads.cpp | 1 + stratosphere/creport/source/creport_utils.cpp | 1 + .../dmnt/source/cheat/dmnt_cheat_service.cpp | 1 + .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 3 +- .../impl/dmnt_cheat_debug_events_manager.cpp | 1 + .../dmnt/source/cheat/impl/dmnt_cheat_vm.cpp | 2 +- stratosphere/dmnt/source/dmnt_main.cpp | 1 + stratosphere/dmnt/source/dmnt_service.hpp | 2 +- .../dmnt/source/dmnt_service_debug.cpp | 1 + .../dmnt/source/dmnt_service_target_io.cpp | 1 + stratosphere/fatal/source/fatal_config.cpp | 25 +-- stratosphere/fatal/source/fatal_debug.cpp | 1 + .../fatal/source/fatal_event_manager.cpp | 1 + stratosphere/fatal/source/fatal_font.cpp | 1 + stratosphere/fatal/source/fatal_main.cpp | 1 + stratosphere/fatal/source/fatal_repair.cpp | 1 + .../fatal/source/fatal_scoped_file.cpp | 1 + stratosphere/fatal/source/fatal_service.cpp | 1 + stratosphere/fatal/source/fatal_task.cpp | 1 + .../fatal/source/fatal_task_clock.cpp | 1 + .../fatal/source/fatal_task_error_report.cpp | 7 +- .../fatal/source/fatal_task_power.cpp | 1 + .../fatal/source/fatal_task_screen.cpp | 15 +- .../fatal/source/fatal_task_sound.cpp | 1 + stratosphere/loader/source/ldr_arguments.cpp | 1 + .../loader/source/ldr_capabilities.cpp | 1 + .../loader/source/ldr_content_management.cpp | 1 + .../loader/source/ldr_development_manager.cpp | 1 + .../loader/source/ldr_launch_record.cpp | 1 + .../loader/source/ldr_loader_service.cpp | 1 + stratosphere/loader/source/ldr_main.cpp | 2 +- stratosphere/loader/source/ldr_meta.cpp | 1 + stratosphere/loader/source/ldr_patcher.cpp | 1 + stratosphere/loader/source/ldr_ro_manager.cpp | 1 + stratosphere/ncm/source/ncm_main.cpp | 1 - stratosphere/pgl/source/pgl_main.cpp | 2 +- .../pm/source/impl/pm_process_info.cpp | 1 + .../pm/source/impl/pm_process_manager.cpp | 1 + .../pm/source/impl/pm_resource_manager.cpp | 1 + .../pm/source/pm_boot_mode_service.cpp | 1 + .../pm/source/pm_debug_monitor_service.cpp | 1 + stratosphere/pm/source/pm_info_service.cpp | 1 + stratosphere/pm/source/pm_main.cpp | 1 + stratosphere/pm/source/pm_shell_service.cpp | 1 + stratosphere/ro/source/impl/ro_nro_utils.cpp | 1 + stratosphere/ro/source/impl/ro_nrr_utils.cpp | 1 + stratosphere/ro/source/impl/ro_patcher.cpp | 1 + .../ro/source/impl/ro_service_impl.cpp | 1 + stratosphere/ro/source/ro_debug_monitor.cpp | 1 + stratosphere/ro/source/ro_main.cpp | 1 + stratosphere/ro/source/ro_service.cpp | 1 + .../sm/source/impl/sm_service_manager.cpp | 1 + stratosphere/sm/source/sm_dmnt_service.cpp | 1 + stratosphere/sm/source/sm_main.cpp | 1 + stratosphere/sm/source/sm_manager_service.cpp | 1 + stratosphere/sm/source/sm_user_service.cpp | 1 + stratosphere/spl/source/spl_api_impl.cpp | 1 + .../spl/source/spl_crypto_service.cpp | 1 + stratosphere/spl/source/spl_ctr_drbg.cpp | 1 + .../spl/source/spl_deprecated_service.cpp | 1 + stratosphere/spl/source/spl_es_service.cpp | 1 + stratosphere/spl/source/spl_fs_service.cpp | 1 + .../spl/source/spl_general_service.cpp | 1 + stratosphere/spl/source/spl_main.cpp | 1 + stratosphere/spl/source/spl_manu_service.cpp | 1 + .../spl/source/spl_random_service.cpp | 1 + stratosphere/spl/source/spl_rsa_service.cpp | 1 + stratosphere/spl/source/spl_ssl_service.cpp | 1 + thermosphere/src/lib/ini.c | 3 + 258 files changed, 723 insertions(+), 804 deletions(-) create mode 100644 libraries/libvapours/include/vapours/util/util_type_traits.hpp diff --git a/exosphere/Makefile b/exosphere/Makefile index 09630e14d..14a03900d 100644 --- a/exosphere/Makefile +++ b/exosphere/Makefile @@ -156,6 +156,8 @@ $(OUTPUT).bin : $(OUTPUT).elf $(OUTPUT).elf : $(OFILES) +my_libc.o: CFLAGS += -fno-builtin + %.elf: @echo linking $(notdir $@) $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ diff --git a/exosphere/src/mmu.h b/exosphere/src/mmu.h index e1ded58fe..ed4cba784 100644 --- a/exosphere/src/mmu.h +++ b/exosphere/src/mmu.h @@ -13,7 +13,7 @@ * 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_MMU_H #define EXOSPHERE_MMU_H @@ -134,7 +134,7 @@ #define TCR_EL2_RSVD (BIT(31) | BIT(23)) #define TCR_EL3_RSVD (BIT(31) | BIT(23)) -static inline void mmu_init_table(uintptr_t *tbl, size_t num_entries) { +static inline void mmu_init_table(volatile uintptr_t *tbl, size_t num_entries) { for(size_t i = 0; i < num_entries / 8; i++) { tbl[i] = MMU_PTE_TYPE_FAULT; } diff --git a/exosphere/src/my_libc.c b/exosphere/src/my_libc.c index 2117f2483..ffd1b595b 100644 --- a/exosphere/src/my_libc.c +++ b/exosphere/src/my_libc.c @@ -75,7 +75,7 @@ __libc_fini_array (void) { size_t count; size_t i; - + count = __fini_array_end - __fini_array_start; for (i = count; i > 0; i--) __fini_array_start[i-1] (); @@ -170,7 +170,7 @@ memmove (void *dst_void, } else { - /* Use optimizing algorithm for a non-destructive copy to closely + /* Use optimizing algorithm for a non-destructive copy to closely match memcpy. If the size is small or either SRC or DST is unaligned, then punt into the byte copy loop. This should be rare. */ if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) @@ -561,7 +561,7 @@ memcmp (const void *m1, s2++; } return 0; -#else +#else unsigned char *s1 = (unsigned char *) m1; unsigned char *s2 = (unsigned char *) m2; unsigned long *a1; @@ -572,13 +572,13 @@ memcmp (const void *m1, not turn up in inner loops. */ if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) { - /* Otherwise, load and compare the blocks of memory one + /* Otherwise, load and compare the blocks of memory one word at a time. */ a1 = (unsigned long*) s1; a2 = (unsigned long*) s2; while (n >= LBLOCKSIZE) { - if (*a1 != *a2) + if (*a1 != *a2) break; a1++; a2++; @@ -703,7 +703,7 @@ strchr (const char *s1, /* FUNCTION <<strcmp>>---character string compare - + INDEX strcmp SYNOPSIS @@ -736,7 +736,7 @@ QUICKREF int strcmp (const char *s1, const char *s2) -{ +{ #if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) while (*s1 != '\0' && *s1 == *s2) { @@ -751,7 +751,7 @@ strcmp (const char *s1, /* If s1 or s2 are unaligned, then compare bytes. */ if (!UNALIGNED (s1, s2)) - { + { /* If s1 and s2 are word-aligned, compare them a word at a time. */ a1 = (unsigned long*)s1; a2 = (unsigned long*)s2; @@ -915,7 +915,7 @@ strlen (const char *str) /* FUNCTION <<strncmp>>---character string compare - + INDEX strncmp SYNOPSIS @@ -944,7 +944,7 @@ QUICKREF #define UNALIGNED(X, Y) \ (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) -int +int strncmp (const char *s1, const char *s2, size_t n) @@ -1103,10 +1103,10 @@ strncpy (char *__restrict dst0, #endif /* not PREFER_SIZE_OVER_SPEED */ } -/* +/* FUNCTION <<strnlen>>---character string length - + INDEX strnlen SYNOPSIS diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index 98fa93e81..3124e7925 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -119,7 +119,7 @@ all: check_rebootstub $(BUILD) check_rebootstub: @$(MAKE) -C $(AMS)/exosphere/rebootstub all -$(BUILD): +$(BUILD): check_rebootstub @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile diff --git a/fusee/fusee-primary/src/fs_utils.c b/fusee/fusee-primary/src/fs_utils.c index 5b9b9f8fd..da561a266 100644 --- a/fusee/fusee-primary/src/fs_utils.c +++ b/fusee/fusee-primary/src/fs_utils.c @@ -23,13 +23,16 @@ FATFS sd_fs; static bool g_sd_mounted = false; static bool g_sd_initialized = false; static bool g_ahb_redirect_enabled = false; +sdmmc_t g_sd_sdmmc; +sdmmc_device_t g_sd_device; + bool mount_sd(void) { /* Already mounted. */ if (g_sd_mounted) return true; - + /* Enable AHB redirection if necessary. */ if (!g_ahb_redirect_enabled) { mc_enable_ahb_redirect(); @@ -41,7 +44,7 @@ bool mount_sd(void) if (sdmmc_device_sd_init(&g_sd_device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_UHS_SDR104)) { g_sd_initialized = true; - + /* Mount SD. */ if (f_mount(&sd_fs, "", 1) == FR_OK) { print(SCREEN_LOG_LEVEL_INFO, "Mounted SD card!\n"); @@ -63,7 +66,7 @@ void unmount_sd(void) sdmmc_device_finish(&g_sd_device); g_sd_mounted = false; } - + /* Disable AHB redirection if necessary. */ if (g_ahb_redirect_enabled) { mc_disable_ahb_redirect(); @@ -81,13 +84,13 @@ uint32_t get_file_size(const char *filename) FIL f; if (f_open(&f, filename, FA_READ) != FR_OK) return 0; - + /* Get the file size. */ uint32_t file_size = f_size(&f); - + /* Close the file. */ f_close(&f); - + return file_size; } @@ -101,10 +104,10 @@ int read_from_file(void *dst, uint32_t dst_size, const char *filename) FIL f; if (f_open(&f, filename, FA_READ) != FR_OK) return 0; - + /* Sync. */ f_sync(&f); - + /* Read from file. */ UINT br = 0; int res = f_read(&f, dst, dst_size, &br); @@ -118,7 +121,7 @@ int write_to_file(void *src, uint32_t src_size, const char *filename) /* SD card hasn't been mounted yet. */ if (!g_sd_mounted) return 0; - + /* Open the file for writing. */ FIL f; if (f_open(&f, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) diff --git a/fusee/fusee-primary/src/fs_utils.h b/fusee/fusee-primary/src/fs_utils.h index 8cb0f326c..1e25b2b82 100644 --- a/fusee/fusee-primary/src/fs_utils.h +++ b/fusee/fusee-primary/src/fs_utils.h @@ -13,7 +13,7 @@ * 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 FUSEE_FS_UTILS_H #define FUSEE_FS_UTILS_H @@ -23,8 +23,8 @@ #include "sdmmc/sdmmc.h" #include "utils.h" -sdmmc_t g_sd_sdmmc; -sdmmc_device_t g_sd_device; +extern sdmmc_t g_sd_sdmmc; +extern sdmmc_device_t g_sd_device; bool mount_sd(void); void unmount_sd(void); diff --git a/fusee/fusee-primary/src/lib/ini.c b/fusee/fusee-primary/src/lib/ini.c index 63626c72d..ccf4640d2 100644 --- a/fusee/fusee-primary/src/lib/ini.c +++ b/fusee/fusee-primary/src/lib/ini.c @@ -70,7 +70,10 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(dest, src, size - 1); +#pragma GCC diagnostic pop dest[size - 1] = '\0'; return dest; } diff --git a/fusee/fusee-secondary/src/lib/ini.c b/fusee/fusee-secondary/src/lib/ini.c index 63626c72d..ccf4640d2 100644 --- a/fusee/fusee-secondary/src/lib/ini.c +++ b/fusee/fusee-secondary/src/lib/ini.c @@ -70,7 +70,10 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(dest, src, size - 1); +#pragma GCC diagnostic pop dest[size - 1] = '\0'; return dest; } diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 07974428f..d82b9b1eb 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -119,10 +119,16 @@ static int emummc_ini_handler(void *user, const char *section, const char *name, } else if (strcmp(name, EMUMMC_ID_KEY) == 0) { sscanf(value, "%lx", &emummc_cfg->id); } else if (strcmp(name, EMUMMC_PATH_KEY) == 0) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(emummc_cfg->path, value, sizeof(emummc_cfg->path) - 1); +#pragma GCC diagnostic pop emummc_cfg->path[sizeof(emummc_cfg->path) - 1] = '\0'; } else if (strcmp(name, EMUMMC_NINTENDO_PATH_KEY) == 0) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(emummc_cfg->nintendo_path, value, sizeof(emummc_cfg->nintendo_path) - 1); +#pragma GCC diagnostic pop emummc_cfg->nintendo_path[sizeof(emummc_cfg->nintendo_path) - 1] = '\0'; } else { return 0; @@ -356,7 +362,10 @@ static bool nxboot_configure_emummc(exo_emummc_config_t *exo_emummc_config) { /* Initialize values from emummc config. */ exo_emummc_config->base_cfg.id = emummc_cfg.id; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(exo_emummc_config->emu_dir_path, emummc_cfg.nintendo_path, sizeof(exo_emummc_config->emu_dir_path)); +#pragma GCC diagnostic pop exo_emummc_config->emu_dir_path[sizeof(exo_emummc_config->emu_dir_path) - 1] = '\0'; if (emummc_cfg.enabled) { @@ -370,7 +379,10 @@ static bool nxboot_configure_emummc(exo_emummc_config_t *exo_emummc_config) { } } else if (is_valid_folder(emummc_cfg.path)) { exo_emummc_config->base_cfg.type = EMUMMC_TYPE_FILES; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(exo_emummc_config->file_cfg.path, emummc_cfg.path, sizeof(exo_emummc_config->file_cfg.path)); +#pragma GCC diagnostic pop exo_emummc_config->file_cfg.path[sizeof(exo_emummc_config->file_cfg.path) - 1] = '\0'; int num_parts = 0; diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 964a7d0aa..4d68b232e 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -18,7 +18,7 @@ export ATMOSPHERE_DEFINES := -DATMOSPHERE export ATMOSPHERE_SETTINGS := -fPIE -g export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector -export ATMOSPHERE_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++17 +export ATMOSPHERE_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++20 export ATMOSPHERE_ASFLAGS := diff --git a/libraries/libmesosphere/Makefile b/libraries/libmesosphere/Makefile index c9c190e90..747fd2856 100644 --- a/libraries/libmesosphere/Makefile +++ b/libraries/libmesosphere/Makefile @@ -6,8 +6,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -#PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/mesosphere.hpp -PRECOMPILED_HEADERS := +PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/mesosphere.hpp DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions @@ -122,6 +121,8 @@ $(filter-out kern_svc_tables.o, $(OFILES)) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) +kern_libc_generic.o: CFLAGS += -fno-builtin + #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin #--------------------------------------------------------------------------------- diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp index a6d7a180e..ce276c1a7 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp @@ -72,7 +72,7 @@ namespace ams::kern::arch::arm64 { SgirTargetListFilter_Reserved = (3 << 24), }; }; - static_assert(std::is_pod<GicDistributor>::value); + static_assert(util::is_pod<GicDistributor>::value); static_assert(sizeof(GicDistributor) == 0x1000); struct GicCpuInterface { @@ -98,7 +98,7 @@ namespace ams::kern::arch::arm64 { u32 dir; u32 _0x1004[1023]; }; - static_assert(std::is_pod<GicCpuInterface>::value); + static_assert(util::is_pod<GicCpuInterface>::value); static_assert(sizeof(GicCpuInterface) == 0x2000); struct KInterruptController { @@ -164,11 +164,11 @@ namespace ams::kern::arch::arm64 { } void SetTarget(s32 irq, s32 core_id) const { - this->gicd->itargetsr.bytes[irq] |= GetGicMask(core_id); + this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] | GetGicMask(core_id); } void ClearTarget(s32 irq, s32 core_id) const { - this->gicd->itargetsr.bytes[irq] &= ~GetGicMask(core_id); + this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] & ~GetGicMask(core_id); } void SetPriorityLevel(s32 irq, s32 level) const { diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp index 620ef85fe..3d379ca03 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp @@ -32,7 +32,7 @@ namespace ams::kern::init { u32 init_array_offset; u32 init_array_end_offset; }; - static_assert(std::is_pod<KernelLayout>::value); + static_assert(util::is_pod<KernelLayout>::value); static_assert(sizeof(KernelLayout) == 0x30); } \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index 2ffc28691..01648d3ec 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -185,10 +185,7 @@ namespace ams::kern { T *obj; private: constexpr ALWAYS_INLINE void Swap(KScopedAutoObject &rhs) { - /* TODO: C++20 constexpr std::swap */ - T *tmp = rhs.obj; - rhs.obj = this->obj; - this->obj = tmp; + std::swap(this->obj, rhs.obj); } public: constexpr ALWAYS_INLINE KScopedAutoObject() : obj(nullptr) { /* ... */ } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index bac44a218..72d48666a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -64,7 +64,7 @@ namespace ams::kern { static constexpr u32 GetCapabilityId(CapabilityType type) { const u32 flag = GetCapabilityFlag(type); - if (true /* C++20: std::is_constant_evaluated() */) { + if (std::is_constant_evaluated()) { return CountTrailingZero(flag); } else { return static_cast<u32>(__builtin_ctz(flag)); @@ -84,7 +84,7 @@ namespace ams::kern { template<CapabilityType Type> static constexpr inline u32 CapabilityId = []() -> u32 { const u32 flag = static_cast<u32>(Type) + 1; - if (true /* C++20: std::is_constant_evaluated() */) { + if (std::is_constant_evaluated()) { for (u32 i = 0; i < BITSIZEOF(u32); i++) { if (flag & (1u << i)) { return i; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp index ae8a7947d..442f04fed 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_current_context.hpp @@ -31,8 +31,10 @@ namespace ams::kern { s32 core_id; void *exception_stack_top; }; - static_assert(std::is_pod<KCurrentContext>::value); + static_assert(std::is_standard_layout<KCurrentContext>::value && std::is_trivially_destructible<KCurrentContext>::value); static_assert(sizeof(KCurrentContext) <= cpu::DataCacheLineSize); + static_assert(sizeof(std::atomic<KThread *>) == sizeof(KThread *)); + static_assert(sizeof(std::atomic<KProcess *>) == sizeof(KProcess *)); namespace impl { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 0ebfb206d..2298dd9dd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -65,7 +65,7 @@ namespace ams::kern { Node *next; u8 buffer[PageSize - sizeof(Node *)]; }; - static_assert(std::is_pod<Node>::value); + static_assert(util::is_pod<Node>::value); private: Node *root; public: diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_priority_queue.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_priority_queue.hpp index 3b471b4d0..033c59373 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_priority_queue.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_priority_queue.hpp @@ -18,39 +18,34 @@ namespace ams::kern { - /* - TODO: C++20 + template<typename T> + concept KPriorityQueueAffinityMask = !std::is_reference<T>::value && requires (T &t) { + { t.GetAffinityMask() } -> std::convertible_to<u64>; + { t.SetAffinityMask(std::declval<u64>()) }; - template<typename T> - concept KPriorityQueueAffinityMask = !std::is_reference<T>::value && requires (T &t) { - { t.GetAffinityMask() } -> std::convertible_to<u64>; - { t.SetAffinityMask(std::declval<u64>()) }; + { t.GetAffinity(std::declval<int32_t>()) } -> std::same_as<bool>; + { t.SetAffinity(std::declval<int32_t>(), std::declval<bool>()) }; + { t.SetAll() }; + }; - { t.GetAffinity(std::declval<int32_t>()) } -> std::same_as<bool>; - { t.SetAffinity(std::declval<int32_t>(), std::declval<bool>()) }; - { t.SetAll() }; - }; + template<typename T> + concept KPriorityQueueMember = !std::is_reference<T>::value && requires (T &t) { + { typename T::QueueEntry() }; + { (typename T::QueueEntry()).Initialize() }; + { (typename T::QueueEntry()).SetPrev(std::addressof(t)) }; + { (typename T::QueueEntry()).SetNext(std::addressof(t)) }; + { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; + { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; + { t.GetPriorityQueueEntry(std::declval<s32>()) } -> std::same_as<typename T::QueueEntry &>; - template<typename T> - concept KPriorityQueueMember = !std::is_reference<T>::value && requires (T &t) { - { typename T::QueueEntry() }; - { (typename T::QueueEntry()).Initialize() }; - { (typename T::QueueEntry()).SetPrev(std::addressof(t)) }; - { (typename T::QueueEntry()).SetNext(std::addressof(t)) }; - { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; - { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; - { t.GetPriorityQueueEntry(std::declval<s32>()) } -> std::same_as<typename T::QueueEntry &>; + { t.GetAffinityMask() }; + { typename std::remove_cvref<decltype(t.GetAffinityMask())>::type() } -> KPriorityQueueAffinityMask; - { t.GetAffinityMask() }; - { typename std::remove_cvref<decltype(t.GetAffinityMask())>::type() } -> KPriorityQueueAffinityMask; + { t.GetActiveCore() } -> std::convertible_to<s32>; + { t.GetPriority() } -> std::convertible_to<s32>; + }; - { t.GetActiveCore() } -> std::convertible_to<s32>; - { t.GetPriority() } -> std::convertible_to<s32>; - }; - */ - - - template<typename Member, size_t _NumCores, int LowestPriority, int HighestPriority> /* TODO C++20: requires KPriorityQueueMember<Member> */ + template<typename Member, size_t _NumCores, int LowestPriority, int HighestPriority> requires KPriorityQueueMember<Member> class KPriorityQueue { public: using AffinityMaskType = typename std::remove_cv<typename std::remove_reference<decltype(std::declval<Member>().GetAffinityMask())>::type>::type; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index 5fb57ad2f..2e8247f5a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -33,8 +33,6 @@ namespace ams::kern { NON_COPYABLE(KScheduler); NON_MOVEABLE(KScheduler); public: - using LockType = KAbstractSchedulerLock<KScheduler>; - static constexpr s32 HighestCoreMigrationAllowedPriority = 2; static_assert(ams::svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority); static_assert(ams::svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority); @@ -50,9 +48,6 @@ namespace ams::kern { private: friend class KScopedSchedulerLock; friend class KScopedSchedulerLockAndSleep; - static bool s_scheduler_update_needed; - static LockType s_scheduler_lock; - static KSchedulerPriorityQueue s_priority_queue; private: SchedulingState state; bool is_active; @@ -160,6 +155,12 @@ namespace ams::kern { } NOINLINE u64 UpdateHighestPriorityThread(KThread *thread); + public: + using LockType = KAbstractSchedulerLock<KScheduler>; + private: + static bool s_scheduler_update_needed; + static KSchedulerPriorityQueue s_priority_queue; + static LockType s_scheduler_lock; }; class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp index 3be30f901..355719f13 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp @@ -23,19 +23,14 @@ namespace ams::kern { class KThread; - /* - TODO: C++20 + template<typename T> + concept KSchedulerLockable = !std::is_reference<T>::value && requires(T) { + { T::DisableScheduling() } -> std::same_as<void>; + { T::EnableScheduling(std::declval<u64>()) } -> std::same_as<void>; + { T::UpdateHighestPriorityThreads() } -> std::convertible_to<u64>; + }; - template<typename T> - concept KSchedulerLockable = !std::is_reference<T>::value && requires { - { T::DisableScheduling() } -> std::same_as<void>; - { T::EnableScheduling(std::declval<u64>()) } -> std::same_as<void>; - { T::UpdateHighestPriorityThreads() } -> std::convertible_to<u64>; - }; - - */ - - template<typename SchedulerType> /* TODO C++20: requires KSchedulerLockable<SchedulerType> */ + template<typename SchedulerType> requires KSchedulerLockable<SchedulerType> class KAbstractSchedulerLock { private: KAlignedSpinLock spin_lock; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scoped_lock.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scoped_lock.hpp index fd6756b2c..8ae0fecbc 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scoped_lock.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scoped_lock.hpp @@ -18,18 +18,13 @@ namespace ams::kern { - /* - TODO: C++20 + template<typename T> + concept KLockable = !std::is_reference<T>::value && requires (T &t) { + { t.Lock() } -> std::same_as<void>; + { t.Unlock() } -> std::same_as<void>; + }; - template<typename T> - concept KLockable = !std::is_reference<T>::value && requires (T &t) { - { t.Lock() } -> std::same_as<void>; - { t.Unlock() } -> std::same_as<void>; - }; - - */ - - template<typename T> /* TODO C++20: requires KLockable<T> */ + template<typename T> requires KLockable<T> class KScopedLock { NON_COPYABLE(KScopedLock); NON_MOVEABLE(KScopedLock); diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_k_user_pointer.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_k_user_pointer.hpp index fce28f594..5d707d52e 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_k_user_pointer.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_k_user_pointer.hpp @@ -22,47 +22,35 @@ namespace ams::kern::svc { namespace impl { - /* TODO: C++20 - template<typename T> - concept Pointer = std::is_pointer<T>::value; - - template<typename T> - concept NonConstPointer = Pointer<T> && !std::is_const<typename std::remove_pointer<T>::type>::value; - - template<typename T> - concept ConstPointer = Pointer<T> && std::is_const<typename std::remove_pointer<T>::type>::value; - - template<typename T, size_t N> - concept AlignedNPointer = Pointer<T> && alignof(typename std::remove_pointer<T>::type) >= N && util::IsAligned(sizeof(typename std::remove_pointer<T>::type), N); - - template<typename T> - concept Aligned8Pointer = AlignedNPointer<T, sizeof(u8)>; - - template<typename T> - concept Aligned16Pointer = AlignedNPointer<T, sizeof(u16)> && Aligned8<T>; - - template<typename T> - concept Aligned32Pointer = AlignedNPointer<T, sizeof(u32)> && Aligned16<T>; - - template<typename T> - concept Aligned64Pointer = AlignedNPointer<T, sizeof(u64)> && Aligned32<T>; - */ + template<typename T> + concept Pointer = std::is_pointer<T>::value; template<typename T> - constexpr inline bool IsPointer = std::is_pointer<T>::value; + concept NonConstPointer = Pointer<T> && !std::is_const<typename std::remove_pointer<T>::type>::value; template<typename T> - constexpr inline bool IsConstPointer = IsPointer<T> && std::is_const<typename std::remove_pointer<T>::type>::value; - - template<typename T> - constexpr inline bool IsNonConstPointer = IsPointer<T> && !std::is_const<typename std::remove_pointer<T>::type>::value; + concept ConstPointer = Pointer<T> && std::is_const<typename std::remove_pointer<T>::type>::value; template<typename T, size_t N> - constexpr inline bool IsAlignedNPointer = IsPointer<T> && alignof(typename std::remove_pointer<T>::type) >= N && util::IsAligned(sizeof(typename std::remove_pointer<T>::type), N); + concept AlignedNPointer = Pointer<T> && alignof(typename std::remove_pointer<T>::type) >= N && util::IsAligned(sizeof(typename std::remove_pointer<T>::type), N); - template<typename _T, typename = void> /* requires Aligned8Pointer<_T> */ - class KUserPointerImplTraits { - static_assert(IsAlignedNPointer<_T, sizeof(u8)>); + template<typename T> + concept Aligned8Pointer = AlignedNPointer<T, sizeof(u8)>; + + template<typename T> + concept Aligned16Pointer = AlignedNPointer<T, sizeof(u16)> && Aligned8Pointer<T>; + + template<typename T> + concept Aligned32Pointer = AlignedNPointer<T, sizeof(u32)> && Aligned16Pointer<T>; + + template<typename T> + concept Aligned64Pointer = AlignedNPointer<T, sizeof(u64)> && Aligned32Pointer<T>; + + template<typename _T> + class KUserPointerImplTraits; + + template<typename _T> requires Aligned8Pointer<_T> + class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const<typename std::remove_pointer<_T>::type>::type; public: @@ -77,9 +65,8 @@ namespace ams::kern::svc { } }; - template<typename _T> /* requires Aligned32Pointer<_T> */ - class KUserPointerImplTraits<_T, typename std::enable_if<IsAlignedNPointer<_T, sizeof(u32)> && !IsAlignedNPointer<_T, sizeof(u64)>>::type> { - static_assert(IsAlignedNPointer<_T, sizeof(u32)>); + template<typename _T> requires Aligned32Pointer<_T> + class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const<typename std::remove_pointer<_T>::type>::type; public: @@ -94,9 +81,8 @@ namespace ams::kern::svc { } }; - template<typename _T> /* requires Aligned64Pointer<_T> */ - class KUserPointerImplTraits<_T, typename std::enable_if<IsAlignedNPointer<_T, sizeof(u64)>>::type> { - static_assert(IsAlignedNPointer<_T, sizeof(u64)>); + template<typename _T> requires Aligned64Pointer<_T> + class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const<typename std::remove_pointer<_T>::type>::type; public: @@ -111,8 +97,11 @@ namespace ams::kern::svc { } }; - template<typename _T> /* requires Aligned8Pointer<_T> */ - class KUserPointerImpl : impl::KUserPointerTag { + template<typename _T> + class KUserPointerImpl; + + template<typename _T> requires Aligned8Pointer<_T> + class KUserPointerImpl<_T> : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits<_T>; protected: @@ -170,11 +159,11 @@ namespace ams::kern::svc { } - template<typename T, typename = void> - class KUserPointer; + template<typename T> + struct KUserPointer; - template<typename T> /* requires impl::ConstPointer<T> */ - struct KUserPointer<T, typename std::enable_if<impl::IsConstPointer<T>>::type> : public impl::KUserPointerImpl<T> { + template<typename T> requires impl::ConstPointer<T> + struct KUserPointer<T> : public impl::KUserPointerImpl<T> { public: static constexpr bool IsInput = true; public: @@ -186,8 +175,8 @@ namespace ams::kern::svc { using impl::KUserPointerImpl<T>::GetUnsafePointer; }; - template<typename T> /* requires impl::NonConstPointer<T> */ - struct KUserPointer<T, typename std::enable_if<impl::IsNonConstPointer<T>>::type> : public impl::KUserPointerImpl<T> { + template<typename T> requires impl::NonConstPointer<T> + struct KUserPointer<T> : public impl::KUserPointerImpl<T> { public: static constexpr bool IsInput = false; public: diff --git a/libraries/libmesosphere/source/kern_k_address_space_info.cpp b/libraries/libmesosphere/source/kern_k_address_space_info.cpp index e8c04ec91..1d81e14d7 100644 --- a/libraries/libmesosphere/source/kern_k_address_space_info.cpp +++ b/libraries/libmesosphere/source/kern_k_address_space_info.cpp @@ -22,19 +22,19 @@ namespace ams::kern { constexpr uintptr_t Invalid = std::numeric_limits<uintptr_t>::max(); constexpr KAddressSpaceInfo AddressSpaceInfos[] = { - { .bit_width = 32, .address = 2_MB, .size = 1_GB - 2_MB, KAddressSpaceInfo::Type_32Bit, }, - { .bit_width = 32, .address = 1_GB, .size = 4_GB - 1_GB, KAddressSpaceInfo::Type_Small64Bit, }, - { .bit_width = 32, .address = Invalid, .size = 1_GB, KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 32, .address = Invalid, .size = 1_GB, KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 36, .address = 128_MB, .size = 2_GB - 128_MB, KAddressSpaceInfo::Type_32Bit, }, - { .bit_width = 36, .address = 2_GB, .size = 64_GB - 2_GB, KAddressSpaceInfo::Type_Small64Bit, }, - { .bit_width = 36, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 36, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 39, .address = 128_MB, .size = 512_GB - 128_MB, KAddressSpaceInfo::Type_Large64Bit, }, - { .bit_width = 39, .address = Invalid, .size = 64_GB, KAddressSpaceInfo::Type_32Bit, }, - { .bit_width = 39, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Heap, }, - { .bit_width = 39, .address = Invalid, .size = 64_GB, KAddressSpaceInfo::Type_Alias, }, - { .bit_width = 39, .address = Invalid, .size = 2_GB, KAddressSpaceInfo::Type_Stack, }, + { .bit_width = 32, .address = 2_MB, .size = 1_GB - 2_MB, .type = KAddressSpaceInfo::Type_32Bit, }, + { .bit_width = 32, .address = 1_GB, .size = 4_GB - 1_GB, .type = KAddressSpaceInfo::Type_Small64Bit, }, + { .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Heap, }, + { .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Alias, }, + { .bit_width = 36, .address = 128_MB, .size = 2_GB - 128_MB, .type = KAddressSpaceInfo::Type_32Bit, }, + { .bit_width = 36, .address = 2_GB, .size = 64_GB - 2_GB, .type = KAddressSpaceInfo::Type_Small64Bit, }, + { .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, }, + { .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Alias, }, + { .bit_width = 39, .address = 128_MB, .size = 512_GB - 128_MB, .type = KAddressSpaceInfo::Type_Large64Bit, }, + { .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_32Bit, }, + { .bit_width = 39, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, }, + { .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_Alias, }, + { .bit_width = 39, .address = Invalid, .size = 2_GB, .type = KAddressSpaceInfo::Type_Stack, }, }; constexpr bool IsAllowedIndexForAddress(size_t index) { diff --git a/libraries/libmesosphere/source/kern_k_object_name.cpp b/libraries/libmesosphere/source/kern_k_object_name.cpp index 763122450..b5fd505a4 100644 --- a/libraries/libmesosphere/source/kern_k_object_name.cpp +++ b/libraries/libmesosphere/source/kern_k_object_name.cpp @@ -19,9 +19,8 @@ namespace ams::kern { namespace { - /* TODO: C++20 constinit */ - KLightLock g_object_list_lock; - KObjectName::List g_object_list; + constinit KLightLock g_object_list_lock; + constinit KObjectName::List g_object_list; } diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile index 15aad5a6c..a87f062f5 100644 --- a/libraries/libstratosphere/Makefile +++ b/libraries/libstratosphere/Makefile @@ -15,6 +15,8 @@ include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- +PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/stratosphere.hpp + DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) @@ -40,7 +42,7 @@ LIBDIRS := $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours ifneq ($(BUILD),$(notdir $(CURDIR))) #--------------------------------------------------------------------------------- -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ @@ -77,6 +79,7 @@ endif export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) @@ -125,6 +128,8 @@ DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- $(OUTPUT) : $(OFILES) +$(OFILES) : $(GCH_FILES) + $(OFILES_SRC) : $(HFILES_BIN) #--------------------------------------------------------------------------------- diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp index 73b69fba7..b16de724b 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp @@ -104,7 +104,7 @@ namespace ams { }; static_assert(sizeof(FatalErrorContext) == 0x450, "sizeof(FatalErrorContext)"); - static_assert(std::is_pod<FatalErrorContext>::value, "FatalErrorContext"); + static_assert(util::is_pod<FatalErrorContext>::value, "FatalErrorContext"); #ifdef ATMOSPHERE_GIT_BRANCH NX_CONSTEXPR const char *GetGitBranch() { diff --git a/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_decode_option.hpp b/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_decode_option.hpp index 5b0ba29e1..6606470b5 100644 --- a/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_decode_option.hpp +++ b/libraries/libstratosphere/include/stratosphere/capsrv/capsrv_screen_shot_decode_option.hpp @@ -41,6 +41,6 @@ namespace ams::capsrv { }; static_assert(sizeof(ScreenShotDecodeOption) == 0x20); static_assert(sizeof(ScreenShotDecodeOption) == sizeof(::CapsScreenShotDecodeOption)); - static_assert(std::is_pod<ScreenShotDecodeOption>::value); + static_assert(util::is_pod<ScreenShotDecodeOption>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp b/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp index 6f2bffc40..b7d5847f0 100644 --- a/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/cfg/cfg_types.hpp @@ -49,7 +49,7 @@ namespace ams::cfg { }; static_assert(sizeof(OverrideStatus) == 0x10, "sizeof(OverrideStatus)"); - static_assert(std::is_pod<OverrideStatus>::value, "std::is_pod<OverrideStatus>::value"); + static_assert(util::is_pod<OverrideStatus>::value, "util::is_pod<OverrideStatus>::value"); constexpr inline bool operator==(const OverrideStatus &lhs, const OverrideStatus &rhs) { return std::memcmp(&lhs, &rhs, sizeof(lhs)) == 0; diff --git a/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp b/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp index 017e73407..53ab40bf8 100644 --- a/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/dmnt/dmnt_cheat_types.hpp @@ -36,7 +36,7 @@ namespace ams::dmnt::cheat { u8 main_nso_build_id[0x20]; }; - static_assert(std::is_pod<CheatProcessMetadata>::value && sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata definition!"); + static_assert(util::is_pod<CheatProcessMetadata>::value && sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata definition!"); struct CheatDefinition : sf::LargeData, sf::PrefersMapAliasTransferMode { char readable_name[0x40]; @@ -50,8 +50,8 @@ namespace ams::dmnt::cheat { CheatDefinition definition; }; - static_assert(std::is_pod<CheatDefinition>::value, "CheatDefinition"); - static_assert(std::is_pod<CheatEntry>::value, "CheatEntry"); + static_assert(util::is_pod<CheatDefinition>::value, "CheatDefinition"); + static_assert(util::is_pod<CheatEntry>::value, "CheatEntry"); struct FrozenAddressValue { u64 value; diff --git a/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp index bc29605e2..d26cf6e16 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp @@ -108,7 +108,7 @@ namespace ams::erpt { }; using ReportFlagSet = util::BitFlagSet<BITSIZEOF(u32), ReportFlag>; - static_assert(std::is_pod<ReportFlagSet>::value); + static_assert(util::is_pod<ReportFlagSet>::value); static_assert(sizeof(ReportFlagSet) == sizeof(u32)); struct ReportInfo { @@ -149,7 +149,7 @@ namespace ams::erpt { }; using AttachmentFlagSet = util::BitFlagSet<BITSIZEOF(u32), AttachmentFlag>; - static_assert(std::is_pod<AttachmentFlagSet>::value); + static_assert(util::is_pod<AttachmentFlagSet>::value); static_assert(sizeof(AttachmentFlagSet) == sizeof(u32)); constexpr inline u32 AttachmentNameSizeMax = 0x20; diff --git a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp index f2a9dc917..1228e7af5 100644 --- a/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fatal/fatal_types.hpp @@ -75,44 +75,44 @@ namespace ams::fatal { static constexpr size_t MaxStackTraceDepth = 0x20; static constexpr const char *RegisterNameStrings[RegisterName_Count] = { - u8"X0", - u8"X1", - u8"X2", - u8"X3", - u8"X4", - u8"X5", - u8"X6", - u8"X7", - u8"X8", - u8"X9", - u8"X10", - u8"X11", - u8"X12", - u8"X13", - u8"X14", - u8"X15", - u8"X16", - u8"X17", - u8"X18", - u8"X19", - u8"X20", - u8"X21", - u8"X22", - u8"X23", - u8"X24", - u8"X25", - u8"X26", - u8"X27", - u8"X28", - u8"FP", - u8"LR", - u8"SP", - u8"PC", - u8"PState", - u8"Afsr0", - u8"Afsr1", - u8"Esr", - u8"Far", + "X0", + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + "X8", + "X9", + "X10", + "X11", + "X12", + "X13", + "X14", + "X15", + "X16", + "X17", + "X18", + "X19", + "X20", + "X21", + "X22", + "X23", + "X24", + "X25", + "X26", + "X27", + "X28", + "FP", + "LR", + "SP", + "PC", + "PState", + "Afsr0", + "Afsr1", + "Esr", + "Far", }; /* Registers, exception context. N left names for these fields in fatal .rodata. */ @@ -209,27 +209,27 @@ namespace ams::fatal { static constexpr size_t MaxStackTraceDepth = 0x20; static constexpr const char *RegisterNameStrings[RegisterName_Count] = { - u8"R0", - u8"R1", - u8"R2", - u8"R3", - u8"R4", - u8"R5", - u8"R6", - u8"R7", - u8"R8", - u8"R9", - u8"R10", - u8"FP", - u8"IP", - u8"LR", - u8"SP", - u8"PC", - u8"PState", - u8"Afsr0", - u8"Afsr1", - u8"Esr", - u8"Far", + "R0", + "R1", + "R2", + "R3", + "R4", + "R5", + "R6", + "R7", + "R8", + "R9", + "R10", + "FP", + "IP", + "LR", + "SP", + "PC", + "PState", + "Afsr0", + "Afsr1", + "Esr", + "Far", }; /* Registers, exception context. N left names for these fields in fatal .rodata. */ @@ -311,9 +311,9 @@ namespace ams::fatal { } }; - static_assert(std::is_pod<aarch64::CpuContext>::value && sizeof(aarch64::CpuContext) == 0x248, "aarch64::CpuContext definition!"); - static_assert(std::is_pod<aarch32::CpuContext>::value && sizeof(aarch32::CpuContext) == 0xE0, "aarch32::CpuContext definition!"); - static_assert(std::is_pod<CpuContext>::value && sizeof(CpuContext) == 0x250, "CpuContext definition!"); + static_assert(util::is_pod<aarch64::CpuContext>::value && sizeof(aarch64::CpuContext) == 0x248, "aarch64::CpuContext definition!"); + static_assert(util::is_pod<aarch32::CpuContext>::value && sizeof(aarch32::CpuContext) == 0xE0, "aarch32::CpuContext definition!"); + static_assert(util::is_pod<CpuContext>::value && sizeof(CpuContext) == 0x250, "CpuContext definition!"); namespace srv { diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp index 4f6a6436d..e91ae9835 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_common.hpp @@ -43,6 +43,6 @@ namespace ams::fs { return this->Get(); } }; - static_assert(std::is_pod<Int64>::value); + static_assert(util::is_pod<Int64>::value); } \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp index d2306154b..25651f7e3 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_hierarchical_rom_file_table.hpp @@ -28,7 +28,7 @@ namespace ams::fs { Position next_dir; Position next_file; }; - static_assert(std::is_pod<FindPosition>::value); + static_assert(util::is_pod<FindPosition>::value); using DirectoryInfo = RomDirectoryInfo; using FileInfo = RomFileInfo; @@ -56,13 +56,13 @@ namespace ams::fs { Position dir; Position file; }; - static_assert(std::is_pod<RomDirectoryEntry>::value); + static_assert(util::is_pod<RomDirectoryEntry>::value); struct RomFileEntry { Position next; FileInfo info; }; - static_assert(std::is_pod<RomFileEntry>::value); + static_assert(util::is_pod<RomFileEntry>::value); static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength; @@ -109,7 +109,7 @@ namespace ams::fs { return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar)); } }; - static_assert(std::is_pod<RomEntryKey>::value); + static_assert(util::is_pod<RomEntryKey>::value); struct EntryKey { RomEntryKey key; @@ -126,7 +126,7 @@ namespace ams::fs { return hash; } }; - static_assert(std::is_pod<EntryKey>::value); + static_assert(util::is_pod<EntryKey>::value); using DirectoryEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomDirectoryEntry>; using FileEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomFileEntry>; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp index 044e17b78..455f1cdff 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_key_value_storage.hpp @@ -31,7 +31,7 @@ namespace ams::fs { BucketIndex ind; Position pos; }; - static_assert(std::is_pod<FindIndex>::value); + static_assert(util::is_pod<FindIndex>::value); private: static constexpr inline Position InvalidPosition = ~Position(); @@ -41,7 +41,7 @@ namespace ams::fs { Position next; u32 size; }; - static_assert(std::is_pod<Element>::value); + static_assert(util::is_pod<Element>::value); private: s64 bucket_count; SubStorage bucket_storage; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp index c57b1fe9d..7fc957c92 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_path_tool.hpp @@ -26,7 +26,7 @@ namespace ams::fs { size_t length; const RomPathChar *path; }; - static_assert(std::is_pod<RomEntryName>::value); + static_assert(util::is_pod<RomEntryName>::value); constexpr void InitializeRomEntryName(RomEntryName *entry) { AMS_ABORT_UNLESS(entry != nullptr); diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp index 86a646e45..f746af65e 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_dbm_rom_types.hpp @@ -34,19 +34,19 @@ namespace ams::fs { s64 file_entry_size; s64 body_offset; }; - static_assert(std::is_pod<RomFileSystemInformation>::value); + static_assert(util::is_pod<RomFileSystemInformation>::value); static_assert(sizeof(RomFileSystemInformation) == 0x50); struct RomDirectoryInfo { /* ... */ }; - static_assert(std::is_pod<RomDirectoryInfo>::value); + static_assert(util::is_pod<RomDirectoryInfo>::value); struct RomFileInfo { Int64 offset; Int64 size; }; - static_assert(std::is_pod<RomFileInfo>::value); + static_assert(util::is_pod<RomFileInfo>::value); namespace RomStringTraits { diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp index 51fab7070..36e7d790b 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_file.hpp @@ -34,7 +34,7 @@ namespace ams::fs { return !(lhs == rhs); } - static_assert(std::is_pod<ReadOption>::value && sizeof(ReadOption) == sizeof(u32)); + static_assert(util::is_pod<ReadOption>::value && sizeof(ReadOption) == sizeof(u32)); struct WriteOption { u32 value; @@ -58,7 +58,7 @@ namespace ams::fs { return !(lhs == rhs); } - static_assert(std::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32)); + static_assert(util::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32)); struct FileHandle { void *handle; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp index cc9a31a2f..03e344745 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp @@ -42,7 +42,7 @@ namespace ams::fs { template<typename T> std::unique_ptr<T, Deleter> MakeUnique() { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); return std::unique_ptr<T, Deleter>(static_cast<T *>(::ams::fs::impl::Allocate(sizeof(T))), Deleter(sizeof(T))); } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp index 5cdc45c21..1c0fbdebf 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp @@ -36,7 +36,7 @@ namespace ams::fs { } }; - static_assert(std::is_pod<QueryRangeInfo>::value); + static_assert(util::is_pod<QueryRangeInfo>::value); static_assert(sizeof(QueryRangeInfo) == 0x40); static_assert(sizeof(QueryRangeInfo) == sizeof(::FsRangeInfo)); diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp index e13bca7bf..9dae55eb5 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_rights_id.hpp @@ -23,7 +23,7 @@ namespace ams::fs { u64 data64[2]; }; static_assert(sizeof(RightsId) == 0x10); - static_assert(std::is_pod<RightsId>::value); + static_assert(util::is_pod<RightsId>::value); inline bool operator==(const RightsId &lhs, const RightsId &rhs) { return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(RightsId)) == 0; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp index 8d49efe3f..e663d67dc 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -51,7 +51,7 @@ namespace ams::fs { struct UserId { u64 data[2]; }; - static_assert(std::is_pod<UserId>::value); + static_assert(util::is_pod<UserId>::value); constexpr inline bool operator<(const UserId &lhs, const UserId &rhs) { if (lhs.data[0] < rhs.data[0]) { @@ -92,7 +92,7 @@ namespace ams::fs { bool pseudo; u8 reserved[0x1A]; }; - static_assert(std::is_pod<SaveDataCreationInfo>::value); + static_assert(util::is_pod<SaveDataCreationInfo>::value); static_assert(sizeof(SaveDataCreationInfo) == 0x40); struct SaveDataAttribute { @@ -154,6 +154,6 @@ namespace ams::fs { u8 unused[0x190]; }; static_assert(sizeof(SaveDataExtraData) == 0x200); - static_assert(std::is_pod<SaveDataExtraData>::value); + static_assert(util::is_pod<SaveDataExtraData>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp index a847a258d..16d7976de 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp @@ -43,7 +43,7 @@ namespace ams::fssrv::sf { } }; - static_assert(std::is_pod<Path>::value && sizeof(Path) == FS_MAX_PATH); + static_assert(util::is_pod<Path>::value && sizeof(Path) == FS_MAX_PATH); using FspPath = Path; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp index 0da255696..dad54e2a6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp @@ -30,7 +30,7 @@ namespace ams::fssystem { class PageList; struct PageEntry { PageEntry *next; }; - static_assert(std::is_pod<PageEntry>::value); + static_assert(util::is_pod<PageEntry>::value); static_assert(sizeof(PageEntry) <= BlockSizeMin); class PageList : public ::ams::fs::impl::Newable { diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_hierarchical_rom_file_table.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_hierarchical_rom_file_table.hpp index 98d27b059..8ee5f4da6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_hierarchical_rom_file_table.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_hierarchical_rom_file_table.hpp @@ -35,7 +35,7 @@ namespace ams::fssystem { Position next_dir; Position next_file; }; - static_assert(std::is_pod<FindPosition>::value); + static_assert(util::is_pod<FindPosition>::value); using DirectoryInfo = RomDirectoryInfo; using FileInfo = RomFileInfo; @@ -63,13 +63,13 @@ namespace ams::fssystem { Position dir; Position file; }; - static_assert(std::is_pod<RomDirectoryEntry>::value); + static_assert(util::is_pod<RomDirectoryEntry>::value); struct RomFileEntry { Position next; FileInfo info; }; - static_assert(std::is_pod<RomFileEntry>::value); + static_assert(util::is_pod<RomFileEntry>::value); static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength; @@ -115,7 +115,7 @@ namespace ams::fssystem { return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar)); } }; - static_assert(std::is_pod<RomEntryKey>::value); + static_assert(util::is_pod<RomEntryKey>::value); struct EntryKey { RomEntryKey key; @@ -132,7 +132,7 @@ namespace ams::fssystem { return hash; } }; - static_assert(std::is_pod<EntryKey>::value); + static_assert(util::is_pod<EntryKey>::value); using DirectoryEntryMapTable = EntryMapTable<DirectoryBucketStorage, DirectoryEntryStorage, RomEntryKey, EntryKey, RomDirectoryEntry>; using FileEntryMapTable = EntryMapTable<FileBucketStorage, FileEntryStorage, RomEntryKey, EntryKey, RomFileEntry>; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp index f47569f68..1bbea7a5c 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp @@ -37,7 +37,7 @@ namespace ams::fssystem { BucketIndex ind; Position pos; }; - static_assert(std::is_pod<FindIndex>::value); + static_assert(util::is_pod<FindIndex>::value); private: static constexpr inline Position InvalidPosition = ~Position(); @@ -47,7 +47,7 @@ namespace ams::fssystem { Position next; u32 size; }; - static_assert(std::is_pod<Element>::value); + static_assert(util::is_pod<Element>::value); private: s64 bucket_offset; u32 bucket_count; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp index d6cd961ac..6c40eac59 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp @@ -26,7 +26,7 @@ namespace ams::fssystem { size_t length; const RomPathChar *path; }; - static_assert(std::is_pod<RomEntryName>::value); + static_assert(util::is_pod<RomEntryName>::value); constexpr void InitializeRomEntryName(RomEntryName *entry) { entry->length = 0; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_types.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_types.hpp index 304cf9dd5..1baf32eb6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_dbm_rom_types.hpp @@ -34,19 +34,19 @@ namespace ams::fssystem { s64 file_entry_size; s64 body_offset; }; - static_assert(std::is_pod<RomFileSystemInformation>::value); + static_assert(util::is_pod<RomFileSystemInformation>::value); static_assert(sizeof(RomFileSystemInformation) == 0x50); struct RomDirectoryInfo { /* ... */ }; - static_assert(std::is_pod<RomDirectoryInfo>::value); + static_assert(util::is_pod<RomDirectoryInfo>::value); struct RomFileInfo { fs::Int64 offset; fs::Int64 size; }; - static_assert(std::is_pod<RomFileInfo>::value); + static_assert(util::is_pod<RomFileInfo>::value); namespace RomStringTraits { diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp index 5e34906d6..a45ad3d02 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp @@ -29,7 +29,7 @@ namespace ams::fssystem { u32 name_offset; u32 reserved; }; - static_assert(std::is_pod<PartitionEntry>::value); + static_assert(util::is_pod<PartitionEntry>::value); #pragma pack(pop) static constexpr const char VersionSignature[] = { 'P', 'F', 'S', '0' }; @@ -52,7 +52,7 @@ namespace ams::fssystem { u64 hash_target_offset; char hash[HashSize]; }; - static_assert(std::is_pod<PartitionEntry>::value); + static_assert(util::is_pod<PartitionEntry>::value); #pragma pack(pop) static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' }; diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp index 52f8b2708..07c7008a9 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_cache.hpp @@ -186,7 +186,7 @@ namespace ams::kvdb { template<class Key, size_t Capacity> class FileKeyValueCache { - static_assert(std::is_pod<Key>::value, "FileKeyValueCache Key must be pod!"); + static_assert(util::is_pod<Key>::value, "FileKeyValueCache Key must be pod!"); static_assert(sizeof(Key) <= FileKeyValueStore::MaxKeySize, "FileKeyValueCache Key is too big!"); public: using LeastRecentlyUsedList = impl::LruList<Key, Capacity>; diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp index ce204dfbe..047562ff1 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_file_key_value_store.hpp @@ -38,7 +38,7 @@ namespace ams::kvdb { size_t key_size; size_t value_size; }; - static_assert(std::is_pod<Entry>::value, "FileKeyValueStore::Entry definition!"); + static_assert(util::is_pod<Entry>::value, "FileKeyValueStore::Entry definition!"); class Cache { private: @@ -83,13 +83,13 @@ namespace ams::kvdb { /* Niceties. */ template<typename Key> Result Get(size_t *out_size, void *out_value, size_t max_out_size, const Key &key) { - static_assert(std::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!"); + static_assert(util::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!"); return this->Get(out_size, out_value, max_out_size, &key, sizeof(Key)); } template<typename Key, typename Value> Result Get(Value *out_value, const Key &key) { - static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!"); + static_assert(util::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!"); size_t size = 0; R_TRY(this->Get(&size, out_value, sizeof(Value), key)); AMS_ABORT_UNLESS(size >= sizeof(Value)); @@ -103,13 +103,13 @@ namespace ams::kvdb { template<typename Key> Result Set(const Key &key, const void *value, size_t value_size) { - static_assert(std::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!"); + static_assert(util::is_pod<Key>::value && sizeof(Key) <= MaxKeySize, "Invalid FileKeyValueStore Key!"); return this->Set(&key, sizeof(Key), value, value_size); } template<typename Key, typename Value> Result Set(const Key &key, const Value &value) { - static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!"); + static_assert(util::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!"); return this->Set(key, &value, sizeof(Value)); } diff --git a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp index 1ec2b2fc7..f64ee96f2 100644 --- a/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp +++ b/libraries/libstratosphere/include/stratosphere/kvdb/kvdb_memory_key_value_store.hpp @@ -26,7 +26,7 @@ namespace ams::kvdb { template<class Key> class MemoryKeyValueStore { - static_assert(std::is_pod<Key>::value, "KeyValueStore Keys must be pod!"); + static_assert(util::is_pod<Key>::value, "KeyValueStore Keys must be pod!"); NON_COPYABLE(MemoryKeyValueStore); NON_MOVEABLE(MemoryKeyValueStore); public: @@ -49,7 +49,7 @@ namespace ams::kvdb { if constexpr (!std::is_same<Value, void>::value) { AMS_ABORT_UNLESS(sizeof(Value) <= this->value_size); /* Ensure we only get pod. */ - static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod"); + static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod"); } return reinterpret_cast<Value *>(this->value); } @@ -60,7 +60,7 @@ namespace ams::kvdb { if constexpr (!std::is_same<Value, void>::value) { AMS_ABORT_UNLESS(sizeof(Value) <= this->value_size); /* Ensure we only get pod. */ - static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod"); + static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod"); } return reinterpret_cast<Value *>(this->value); } @@ -366,14 +366,14 @@ namespace ams::kvdb { template<typename Value> Result Set(const Key &key, const Value &value) { /* Only allow setting pod. */ - static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod"); + static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod"); return this->Set(key, &value, sizeof(Value)); } template<typename Value> Result Set(const Key &key, const Value *value) { /* Only allow setting pod. */ - static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod"); + static_assert(util::is_pod<Value>::value, "KeyValueStore Values must be pod"); return this->Set(key, value, sizeof(Value)); } diff --git a/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp b/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp index 933fc1d99..4c83687c4 100644 --- a/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ldr/ldr_types.hpp @@ -35,7 +35,7 @@ namespace ams::ldr { u32 aci_fah_size; u8 ac_buffer[0x3E0]; }; - static_assert(std::is_pod<ProgramInfo>::value && sizeof(ProgramInfo) == 0x400, "ProgramInfo definition!"); + static_assert(util::is_pod<ProgramInfo>::value && sizeof(ProgramInfo) == 0x400, "ProgramInfo definition!"); enum ProgramInfoFlag { ProgramInfoFlag_SystemModule = (0 << 0), @@ -71,7 +71,7 @@ namespace ams::ldr { inline bool operator!=(const PinId &lhs, const PinId &rhs) { return lhs.value != rhs.value; } - static_assert(sizeof(PinId) == sizeof(u64) && std::is_pod<PinId>::value, "PinId definition!"); + static_assert(sizeof(PinId) == sizeof(u64) && util::is_pod<PinId>::value, "PinId definition!"); /* Import ModuleInfo from libnx. */ using ModuleInfo = ::LoaderModuleInfo; @@ -142,7 +142,7 @@ namespace ams::ldr { }; }; }; - static_assert(sizeof(NsoHeader) == 0x100 && std::is_pod<NsoHeader>::value, "NsoHeader definition!"); + static_assert(sizeof(NsoHeader) == 0x100 && util::is_pod<NsoHeader>::value, "NsoHeader definition!"); /* NPDM types. */ struct Aci { @@ -160,7 +160,7 @@ namespace ams::ldr { u32 kac_size; u8 reserved_38[0x8]; }; - static_assert(sizeof(Aci) == 0x40 && std::is_pod<Aci>::value, "Aci definition!"); + static_assert(sizeof(Aci) == 0x40 && util::is_pod<Aci>::value, "Aci definition!"); struct Acid { static constexpr u32 Magic = util::FourCC<'A','C','I','D'>::Code; @@ -199,7 +199,7 @@ namespace ams::ldr { u32 kac_size; u8 reserved_238[0x8]; }; - static_assert(sizeof(Acid) == 0x240 && std::is_pod<Acid>::value, "Acid definition!"); + static_assert(sizeof(Acid) == 0x240 && util::is_pod<Acid>::value, "Acid definition!"); struct Npdm { static constexpr u32 Magic = util::FourCC<'M','E','T','A'>::Code; @@ -239,6 +239,6 @@ namespace ams::ldr { u32 acid_offset; u32 acid_size; }; - static_assert(sizeof(Npdm) == 0x80 && std::is_pod<Npdm>::value, "Npdm definition!"); + static_assert(sizeof(Npdm) == 0x80 && util::is_pod<Npdm>::value, "Npdm definition!"); } diff --git a/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp b/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp index 5b15c5135..4a684470e 100644 --- a/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/lr/lr_types.hpp @@ -56,6 +56,6 @@ namespace ams::lr { } }; - static_assert(std::is_pod<Path>::value && sizeof(Path) == fs::EntryNameLengthMax); + static_assert(util::is_pod<Path>::value && sizeof(Path) == fs::EntryNameLengthMax); } diff --git a/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp index ff0bbaf56..314a3d3b5 100644 --- a/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp +++ b/libraries/libstratosphere/include/stratosphere/mem/impl/mem_impl_common.hpp @@ -65,6 +65,6 @@ namespace ams::mem::impl { size_t alloc_size; size_t hash; }; - static_assert(std::is_pod<HeapHash>::value); + static_assert(util::is_pod<HeapHash>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp index c0fcd21f8..16f784289 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_info.hpp @@ -55,7 +55,7 @@ namespace ams::ncm { } }; - static_assert(sizeof(std::is_pod<ContentInfo>::value)); + static_assert(sizeof(util::is_pod<ContentInfo>::value)); static_assert(sizeof(ContentInfo) == 0x18); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index 9f4fd8ae2..244aa3ffb 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -65,7 +65,7 @@ namespace ams::ncm { u32 flags; fs::SaveDataSpaceId space_id; }; - static_assert(std::is_pod<SystemSaveDataInfo>::value); + static_assert(util::is_pod<SystemSaveDataInfo>::value); class ContentManagerImpl final : public IContentManager { private: diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp index 3e2e53567..097f83eb3 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_program_location.hpp @@ -27,7 +27,7 @@ namespace ams::ncm { return { .program_id = program_id, .storage_id = static_cast<u8>(storage_id), }; } }; - static_assert(sizeof(ProgramLocation) == 0x10 && std::is_pod<ProgramLocation>::value); + static_assert(sizeof(ProgramLocation) == 0x10 && util::is_pod<ProgramLocation>::value); static_assert(sizeof(ProgramLocation) == sizeof(::NcmProgramLocation) && alignof(ProgramLocation) == alignof(::NcmProgramLocation), "ProgramLocation Libnx Compatibility"); diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp index 6806fa7f5..f025407c2 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_rights_id.hpp @@ -25,7 +25,7 @@ namespace ams::ncm { u8 reserved[7]; }; static_assert(sizeof(RightsId) == 0x18); - static_assert(std::is_pod<RightsId>::value); + static_assert(util::is_pod<RightsId>::value); inline bool operator==(const RightsId &lhs, const RightsId &rhs) { return std::tie(lhs.id, lhs.key_generation) == std::tie(rhs.id, rhs.key_generation); diff --git a/libraries/libstratosphere/include/stratosphere/os/os_rw_lock_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_rw_lock_types.hpp index 4d6acf17e..166d9de8d 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_rw_lock_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_rw_lock_types.hpp @@ -36,7 +36,7 @@ namespace ams::os { }; util::BitPack32 counter; }; - static_assert(std::is_pod<LockCount>::value); + static_assert(util::is_pod<LockCount>::value); static_assert(std::is_trivial<LockCount>::value); union { diff --git a/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp b/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp index 08aceb8f8..3cbd21b2c 100644 --- a/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pgl/pgl_types.hpp @@ -52,6 +52,6 @@ namespace ams::pgl { }; } }; - static_assert(sizeof(ContentMetaInfo) == 0x10 && std::is_pod<ContentMetaInfo>::value); + static_assert(sizeof(ContentMetaInfo) == 0x10 && util::is_pod<ContentMetaInfo>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp index f433bddb4..a26fe88aa 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp @@ -97,6 +97,6 @@ namespace ams::pm { u32 event; os::ProcessId process_id; }; - static_assert(sizeof(ProcessEventInfo) == 0x10 && std::is_pod<ProcessEventInfo>::value, "ProcessEventInfo definition!"); + static_assert(sizeof(ProcessEventInfo) == 0x10 && util::is_pod<ProcessEventInfo>::value, "ProcessEventInfo definition!"); } diff --git a/libraries/libstratosphere/include/stratosphere/reg.hpp b/libraries/libstratosphere/include/stratosphere/reg.hpp index 6321d53b8..6b5076f93 100644 --- a/libraries/libstratosphere/include/stratosphere/reg.hpp +++ b/libraries/libstratosphere/include/stratosphere/reg.hpp @@ -36,7 +36,7 @@ namespace ams::reg { } inline void SetBits(volatile u32 *reg, u32 mask) { - *reg |= mask; + *reg = *reg | mask; } inline void SetBits(uintptr_t reg, u32 mask) { @@ -44,7 +44,7 @@ namespace ams::reg { } inline void ClearBits(volatile u32 *reg, u32 mask) { - *reg &= ~mask; + *reg = *reg & ~mask; } inline void ClearBits(uintptr_t reg, u32 mask) { @@ -52,7 +52,7 @@ namespace ams::reg { } inline void MaskBits(volatile u32 *reg, u32 mask) { - *reg &= mask; + *reg = *reg & mask; } inline void MaskBits(uintptr_t reg, u32 mask) { diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_device_certificate.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_device_certificate.hpp index b9be05258..4c445a951 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_device_certificate.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_device_certificate.hpp @@ -22,18 +22,18 @@ namespace ams::settings::factory { u8 data[0x180]; }; static_assert(sizeof(EccP256DeviceCertificate) == 0x180); - static_assert(std::is_pod<EccP256DeviceCertificate>::value); + static_assert(util::is_pod<EccP256DeviceCertificate>::value); struct EccB233DeviceCertificate { u8 data[0x180]; }; static_assert(sizeof(EccB233DeviceCertificate) == 0x180); - static_assert(std::is_pod<EccB233DeviceCertificate>::value); + static_assert(util::is_pod<EccB233DeviceCertificate>::value); struct Rsa2048DeviceCertificate { u8 data[0x240]; }; static_assert(sizeof(Rsa2048DeviceCertificate) == 0x240); - static_assert(std::is_pod<Rsa2048DeviceCertificate>::value); + static_assert(util::is_pod<Rsa2048DeviceCertificate>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp index 068d7d655..301139cca 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/factory/settings_serial_number.hpp @@ -22,6 +22,6 @@ namespace ams::settings::factory { char str[0x18]; }; static_assert(sizeof(SerialNumber) == 0x18); - static_assert(std::is_pod<SerialNumber>::value); + static_assert(util::is_pod<SerialNumber>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/settings/settings_fwdbg_types.hpp b/libraries/libstratosphere/include/stratosphere/settings/settings_fwdbg_types.hpp index 461e36b0a..7fee0800c 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/settings_fwdbg_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/settings_fwdbg_types.hpp @@ -27,12 +27,12 @@ namespace ams::settings::fwdbg { char value[util::AlignUp(SettingsNameLengthMax + 1, alignof(u64))]; }; - static_assert(std::is_pod<SettingsName>::value && sizeof(SettingsName) > SettingsNameLengthMax); + static_assert(util::is_pod<SettingsName>::value && sizeof(SettingsName) > SettingsNameLengthMax); struct SettingsItemKey : sf::LargeData { char value[util::AlignUp(SettingsItemKeyLengthMax + 1, alignof(u64))]; }; - static_assert(std::is_pod<SettingsItemKey>::value && sizeof(SettingsItemKey) > SettingsItemKeyLengthMax); + static_assert(util::is_pod<SettingsItemKey>::value && sizeof(SettingsItemKey) > SettingsItemKeyLengthMax); } diff --git a/libraries/libstratosphere/include/stratosphere/settings/settings_types.hpp b/libraries/libstratosphere/include/stratosphere/settings/settings_types.hpp index 0b9600624..2930d1ffd 100644 --- a/libraries/libstratosphere/include/stratosphere/settings/settings_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings/settings_types.hpp @@ -152,7 +152,7 @@ namespace ams::settings { return impl::IsValidLanguageCode(lc, std::make_index_sequence<Language_Count>{}); } - static_assert(std::is_pod<LanguageCode>::value); + static_assert(util::is_pod<LanguageCode>::value); static_assert(sizeof(LanguageCode) == sizeof(u64)); /* Not an official type, but convenient. */ @@ -193,7 +193,7 @@ namespace ams::settings { } }; - static_assert(std::is_pod<FirmwareVersion>::value); + static_assert(util::is_pod<FirmwareVersion>::value); static_assert(sizeof(FirmwareVersion) == sizeof(::SetSysFirmwareVersion)); constexpr inline bool operator==(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp index 65dd2701d..f96c6fd49 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp @@ -63,7 +63,7 @@ namespace ams::sf::cmif { } }; - static_assert(std::is_pod<ServerMessageRuntimeMetadata>::value, "std::is_pod<ServerMessageRuntimeMetadata>::value"); + static_assert(util::is_pod<ServerMessageRuntimeMetadata>::value, "util::is_pod<ServerMessageRuntimeMetadata>::value"); static_assert(sizeof(ServerMessageRuntimeMetadata) == sizeof(u64), "sizeof(ServerMessageRuntimeMetadata)"); class ServerMessageProcessor { diff --git a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp index 44e35b4f7..cca03664c 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp @@ -61,7 +61,7 @@ namespace ams::sf::cmif { return this->handler; } }; - static_assert(std::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)"); + static_assert(util::is_pod<ServiceCommandMeta>::value && sizeof(ServiceCommandMeta) == 0x18, "sizeof(ServiceCommandMeta)"); namespace impl { diff --git a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp index 3ceb664d8..2247d6de5 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -77,7 +77,7 @@ namespace ams::sf { class Out<std::shared_ptr<ServiceImpl>> : public impl::OutObjectTag { static_assert(std::is_base_of<sf::IServiceObject, ServiceImpl>::value, "Out<std::shared_ptr<ServiceImpl>> requires ServiceObject base."); - template<typename, typename> + template<typename> friend class Out; public: @@ -308,11 +308,7 @@ namespace ams::sf::impl { /* Use insertion sort, which is stable and optimal for small numbers of parameters. */ for (size_t i = 1; i < sizeof...(Ts); i++) { for (size_t j = i; j > 0 && values[map[j-1]] > values[map[j]]; j--) { - /* std::swap is not constexpr until c++20 :( */ - /* TODO: std::swap(map[j], map[j-1]); */ - const size_t tmp = map[j]; - map[j] = map[j-1]; - map[j-1] = tmp; + std::swap(map[j], map[j-1]); } } } @@ -1042,7 +1038,7 @@ namespace ams::sf::impl { }; constexpr Result GetCmifOutHeaderPointer(CmifOutHeader **out_header_ptr, cmif::PointerAndSize &out_raw_data) { - CmifOutHeader *header = reinterpret_cast<CmifOutHeader *>(out_raw_data.GetPointer()); + CmifOutHeader *header = static_cast<CmifOutHeader *>(out_raw_data.GetPointer()); R_UNLESS(out_raw_data.GetSize() >= sizeof(*header), sf::cmif::ResultInvalidHeaderSize()); out_raw_data = cmif::PointerAndSize(out_raw_data.GetAddress() + sizeof(*header), out_raw_data.GetSize() - sizeof(*header)); *out_header_ptr = header; diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_out.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_out.hpp index b5fb1e6f1..414c916da 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_out.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_out.hpp @@ -33,9 +33,9 @@ namespace ams::sf { struct IsOutForceEnabled<::ams::Result> : public std::true_type{}; template<typename T> - using IsOutEnabled = typename std::enable_if<std::is_trivial<T>::value || IsOutForceEnabled<T>::value>::type; + concept OutEnabled = (std::is_trivial<T>::value || IsOutForceEnabled<T>::value) && !std::is_pointer<T>::value; - template<typename T, typename = IsOutEnabled<T>> + template<typename T> requires OutEnabled<T> class Out : public impl::OutBaseTag { public: static constexpr size_t TypeSize = sizeof(T); diff --git a/libraries/libstratosphere/source/ams/ams_emummc_api.cpp b/libraries/libstratosphere/source/ams/ams_emummc_api.cpp index f00f86535..f1c1c8880 100644 --- a/libraries/libstratosphere/source/ams/ams_emummc_api.cpp +++ b/libraries/libstratosphere/source/ams/ams_emummc_api.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::emummc { diff --git a/libraries/libstratosphere/source/ams/ams_environment.cpp b/libraries/libstratosphere/source/ams/ams_environment.cpp index 47babfb92..7679570a1 100644 --- a/libraries/libstratosphere/source/ams/ams_environment.cpp +++ b/libraries/libstratosphere/source/ams/ams_environment.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> #include "ams_bpc.h" @@ -182,6 +181,9 @@ extern "C" { /* Custom abort handler, so that std::abort will trigger these. */ void abort() { + static ams::os::Mutex abort_lock(true); + std::scoped_lock lk(abort_lock); + ams::AbortImpl(); __builtin_unreachable(); } diff --git a/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp b/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp index 63243d66a..e964d3931 100644 --- a/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp +++ b/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> #include <stratosphere/spl.hpp> #include <stratosphere/spl/smc/spl_smc.hpp> diff --git a/libraries/libstratosphere/source/capsrv/server/decodersrv/decodersrv_decoder_work_memory.hpp b/libraries/libstratosphere/source/capsrv/server/decodersrv/decodersrv_decoder_work_memory.hpp index cf590c573..11b380882 100644 --- a/libraries/libstratosphere/source/capsrv/server/decodersrv/decodersrv_decoder_work_memory.hpp +++ b/libraries/libstratosphere/source/capsrv/server/decodersrv/decodersrv_decoder_work_memory.hpp @@ -23,6 +23,6 @@ namespace ams::capsrv::server { }; static_assert(sizeof(DecoderWorkMemory) == SoftwareJpegDecoderWorkMemorySize); static_assert(alignof(DecoderWorkMemory) == os::MemoryPageSize); - static_assert(std::is_pod<DecoderWorkMemory>::value); + static_assert(util::is_pod<DecoderWorkMemory>::value); } diff --git a/libraries/libstratosphere/source/cfg/cfg_flags.cpp b/libraries/libstratosphere/source/cfg/cfg_flags.cpp index c607cbabc..abf078a09 100644 --- a/libraries/libstratosphere/source/cfg/cfg_flags.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_flags.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::cfg { diff --git a/libraries/libstratosphere/source/cfg/cfg_override.cpp b/libraries/libstratosphere/source/cfg/cfg_override.cpp index c31cb16b4..9bd7fedb6 100644 --- a/libraries/libstratosphere/source/cfg/cfg_override.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_override.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::cfg { diff --git a/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp b/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp index 1df518d00..0fe262a8b 100644 --- a/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_privileged_process.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::cfg { diff --git a/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp b/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp index a79a23fa7..cbf60a724 100644 --- a/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp +++ b/libraries/libstratosphere/source/cfg/cfg_sd_card.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::cfg { diff --git a/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp b/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp index bbc580839..3fa00fbcb 100644 --- a/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp +++ b/libraries/libstratosphere/source/fs/fs_file_path_hash.hpp @@ -23,7 +23,7 @@ namespace ams::fs::impl { struct FilePathHash : public Newable { u8 data[FilePathHashSize]; }; - static_assert(std::is_pod<FilePathHash>::value); + static_assert(util::is_pod<FilePathHash>::value); inline bool operator==(const FilePathHash &lhs, const FilePathHash &rhs) { return std::memcmp(lhs.data, rhs.data, FilePathHashSize) == 0; diff --git a/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp b/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp index 8f77d3fe2..de999277c 100644 --- a/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp +++ b/libraries/libstratosphere/source/fs/fsa/fs_mount_name.hpp @@ -21,6 +21,6 @@ namespace ams::fs { struct MountName { char str[MountNameLengthMax + 1]; }; - static_assert(std::is_pod<MountName>::value); + static_assert(util::is_pod<MountName>::value); } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp index dc598e492..591c35a9d 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp @@ -24,7 +24,7 @@ namespace ams::fssystem { u32 name_table_size; u32 reserved; }; - static_assert(std::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value); + static_assert(util::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value); static_assert(sizeof(PartitionFileSystemMeta::PartitionFileSystemHeader) == 0x10); template <typename Format> diff --git a/libraries/libstratosphere/source/fssystem/fssystem_romfs_filesystem.cpp b/libraries/libstratosphere/source/fssystem/fssystem_romfs_filesystem.cpp index 93659d160..009c51d06 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_romfs_filesystem.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_romfs_filesystem.cpp @@ -37,7 +37,7 @@ namespace ams::fssystem { virtual ~RomFsFile() { /* ... */ } public: virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override { - R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result { + R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result { size_t read_size = 0; R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read)); @@ -84,7 +84,7 @@ namespace ams::fssystem { operate_size = this->GetSize() - offset; } - R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result { + R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result { R_TRY(this->parent->GetBaseStorage()->OperateRange(dst, dst_size, op_id, this->start + offset, operate_size, src, src_size)); return ResultSuccess(); }, AMS_CURRENT_FUNCTION_NAME)); @@ -113,7 +113,7 @@ namespace ams::fssystem { virtual ~RomFsDirectory() override { /* ... */ } public: virtual Result ReadImpl(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) { - R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result { + R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result { return this->ReadImpl(out_count, std::addressof(this->current_find), out_entries, max_entries); }, AMS_CURRENT_FUNCTION_NAME)); return ResultSuccess(); @@ -278,7 +278,7 @@ namespace ams::fssystem { } Result RomFsFileSystem::GetFileInfo(RomFileTable::FileInfo *out, const char *path) { - R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result { + R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result { R_TRY_CATCH(this->rom_file_table.OpenFile(out, path)) { R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound()); } R_END_TRY_CATCH; @@ -325,7 +325,7 @@ namespace ams::fssystem { } Result RomFsFileSystem::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) { - R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=]() -> Result { + R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result { RomDirectoryInfo dir_info; R_TRY_CATCH(this->rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) { diff --git a/libraries/libstratosphere/source/kvdb/kvdb_archive.cpp b/libraries/libstratosphere/source/kvdb/kvdb_archive.cpp index f3f365752..350704511 100644 --- a/libraries/libstratosphere/source/kvdb/kvdb_archive.cpp +++ b/libraries/libstratosphere/source/kvdb/kvdb_archive.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::kvdb { @@ -42,7 +41,7 @@ namespace ams::kvdb { return header; } }; - static_assert(sizeof(ArchiveHeader) == 0xC && std::is_pod<ArchiveHeader>::value, "ArchiveHeader definition!"); + static_assert(sizeof(ArchiveHeader) == 0xC && util::is_pod<ArchiveHeader>::value, "ArchiveHeader definition!"); struct ArchiveEntryHeader { u8 magic[sizeof(ArchiveEntryMagic)]; @@ -62,7 +61,7 @@ namespace ams::kvdb { return header; } }; - static_assert(sizeof(ArchiveEntryHeader) == 0xC && std::is_pod<ArchiveEntryHeader>::value, "ArchiveEntryHeader definition!"); + static_assert(sizeof(ArchiveEntryHeader) == 0xC && util::is_pod<ArchiveEntryHeader>::value, "ArchiveEntryHeader definition!"); } diff --git a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp index eb699b600..fbcea57ab 100644 --- a/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp +++ b/libraries/libstratosphere/source/kvdb/kvdb_file_key_value_store.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::kvdb { diff --git a/libraries/libstratosphere/source/map/map_api.cpp b/libraries/libstratosphere/source/map/map_api.cpp index 5c3bdf0f9..1c7a50b93 100644 --- a/libraries/libstratosphere/source/map/map_api.cpp +++ b/libraries/libstratosphere/source/map/map_api.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> namespace ams::map { diff --git a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_static.hpp b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_static.hpp index eace75938..523aac2ed 100644 --- a/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_static.hpp +++ b/libraries/libstratosphere/source/mem/impl/heap/mem_impl_heap_tls_heap_static.hpp @@ -160,7 +160,7 @@ namespace ams::mem::impl::heap { template<typename T> static ALWAYS_INLINE T *AlignUpPage(T *ptr) { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); static_assert(util::IsAligned(PageSize, alignof(T))); return reinterpret_cast<T *>(AlignUpPage(reinterpret_cast<uintptr_t>(ptr))); } @@ -171,7 +171,7 @@ namespace ams::mem::impl::heap { template<typename T> static ALWAYS_INLINE T *AlignDownPage(T *ptr) { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); static_assert(util::IsAligned(PageSize, alignof(T))); return reinterpret_cast<T *>(AlignDownPage(reinterpret_cast<uintptr_t>(ptr))); } @@ -182,7 +182,7 @@ namespace ams::mem::impl::heap { template<typename T> static ALWAYS_INLINE T *AlignUpPhysicalPage(T *ptr) { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); static_assert(util::IsAligned(PhysicalPageSize, alignof(T))); return reinterpret_cast<T *>(AlignUpPhysicalPage(reinterpret_cast<uintptr_t>(ptr))); } @@ -193,7 +193,7 @@ namespace ams::mem::impl::heap { template<typename T> static ALWAYS_INLINE T *AlignDownPhysicalPage(T *ptr) { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); static_assert(util::IsAligned(PhysicalPageSize, alignof(T))); return reinterpret_cast<T *>(AlignDownPhysicalPage(reinterpret_cast<uintptr_t>(ptr))); } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp index a9cad8536..30d5ac10d 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp @@ -451,7 +451,7 @@ namespace ams::ncm { /* Ensure this type of key has an owner. */ R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch || key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey()); - + /* Applications are their own owner. */ if (key.type == ContentMetaType::Application) { out_id.SetValue({key.id}); diff --git a/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp index 6585ea6c4..6ff528a31 100644 --- a/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp +++ b/libraries/libstratosphere/source/os/impl/os_resource_manager.cpp @@ -18,7 +18,6 @@ namespace ams::os::impl { - /* TODO: C++20 constinit */ - TYPED_STORAGE(OsResourceManager) ResourceManagerHolder::s_resource_manager_storage = {}; + constinit TYPED_STORAGE(OsResourceManager) ResourceManagerHolder::s_resource_manager_storage = {}; } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp index 4da762eb6..e5a2a48db 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "os_waitable_manager_impl.hpp" #include "os_waitable_object_list.hpp" #include "os_tick_manager.hpp" diff --git a/libraries/libstratosphere/source/os/os_event.cpp b/libraries/libstratosphere/source/os/os_event.cpp index 372e6fd64..2350b8ae0 100644 --- a/libraries/libstratosphere/source/os/os_event.cpp +++ b/libraries/libstratosphere/source/os/os_event.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "impl/os_timeout_helper.hpp" #include "impl/os_waitable_object_list.hpp" #include "impl/os_waitable_holder_impl.hpp" diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp index e98231930..e2bd5811f 100644 --- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp +++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "impl/os_interrupt_event_impl.hpp" #include "impl/os_waitable_object_list.hpp" diff --git a/libraries/libstratosphere/source/os/os_message_queue.cpp b/libraries/libstratosphere/source/os/os_message_queue.cpp index 795ecf4a9..b9bdb0255 100644 --- a/libraries/libstratosphere/source/os/os_message_queue.cpp +++ b/libraries/libstratosphere/source/os/os_message_queue.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "impl/os_waitable_object_list.hpp" #include "impl/os_timeout_helper.hpp" diff --git a/libraries/libstratosphere/source/os/os_semaphore.cpp b/libraries/libstratosphere/source/os/os_semaphore.cpp index e55691095..62fe78d24 100644 --- a/libraries/libstratosphere/source/os/os_semaphore.cpp +++ b/libraries/libstratosphere/source/os/os_semaphore.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "impl/os_waitable_object_list.hpp" #include "impl/os_timeout_helper.hpp" diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp index 9ffbb85a1..633e28aec 100644 --- a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp @@ -27,7 +27,7 @@ namespace ams::pgl::srv { os::ProcessId process_id; u32 flags; }; - static_assert(std::is_pod<ProcessData>::value); + static_assert(util::is_pod<ProcessData>::value); enum ProcessDataFlag : u32 { ProcessDataFlag_None = 0, diff --git a/libraries/libstratosphere/source/sm/sm_api.cpp b/libraries/libstratosphere/source/sm/sm_api.cpp index 4a7b80cc0..ef7be4e54 100644 --- a/libraries/libstratosphere/source/sm/sm_api.cpp +++ b/libraries/libstratosphere/source/sm/sm_api.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_utils.hpp" namespace ams::sm { diff --git a/libraries/libstratosphere/source/sm/sm_manager_api.cpp b/libraries/libstratosphere/source/sm/sm_manager_api.cpp index 383e02952..5072f3c5b 100644 --- a/libraries/libstratosphere/source/sm/sm_manager_api.cpp +++ b/libraries/libstratosphere/source/sm/sm_manager_api.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_utils.hpp" #include "smm_ams.h" diff --git a/libraries/libstratosphere/source/sm/sm_mitm_api.cpp b/libraries/libstratosphere/source/sm/sm_mitm_api.cpp index 38bffd8f6..431c3b96c 100644 --- a/libraries/libstratosphere/source/sm/sm_mitm_api.cpp +++ b/libraries/libstratosphere/source/sm/sm_mitm_api.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_utils.hpp" namespace ams::sm::mitm { diff --git a/libraries/libstratosphere/source/sm/sm_utils.cpp b/libraries/libstratosphere/source/sm/sm_utils.cpp index 62d79f3c1..2cfefc23c 100644 --- a/libraries/libstratosphere/source/sm/sm_utils.cpp +++ b/libraries/libstratosphere/source/sm/sm_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_utils.hpp" namespace ams::sm::impl { diff --git a/libraries/libstratosphere/source/updater/updater_api.cpp b/libraries/libstratosphere/source/updater/updater_api.cpp index 00894124f..014ea117e 100644 --- a/libraries/libstratosphere/source/updater/updater_api.cpp +++ b/libraries/libstratosphere/source/updater/updater_api.cpp @@ -13,8 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include <switch.h> #include <stratosphere.hpp> #include <stratosphere/updater.hpp> diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.cpp b/libraries/libstratosphere/source/updater/updater_bis_management.cpp index 9e5879918..b643634bf 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.cpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "updater_bis_management.hpp" namespace ams::updater { diff --git a/libraries/libstratosphere/source/updater/updater_bis_save.cpp b/libraries/libstratosphere/source/updater/updater_bis_save.cpp index 20618f49e..1ab76e0c4 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_save.cpp +++ b/libraries/libstratosphere/source/updater/updater_bis_save.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "updater_bis_save.hpp" namespace ams::updater { diff --git a/libraries/libstratosphere/source/updater/updater_files.cpp b/libraries/libstratosphere/source/updater/updater_files.cpp index 3266862f9..3d6681816 100644 --- a/libraries/libstratosphere/source/updater/updater_files.cpp +++ b/libraries/libstratosphere/source/updater/updater_files.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "updater_files.hpp" namespace ams::updater { diff --git a/libraries/libstratosphere/source/updater/updater_paths.cpp b/libraries/libstratosphere/source/updater/updater_paths.cpp index 6ede7d00d..4fefa5dfc 100644 --- a/libraries/libstratosphere/source/updater/updater_paths.cpp +++ b/libraries/libstratosphere/source/updater/updater_paths.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "updater_paths.hpp" namespace ams::updater { diff --git a/libraries/libstratosphere/source/util/ini.c b/libraries/libstratosphere/source/util/ini.c index 426430c9b..501e9224f 100644 --- a/libraries/libstratosphere/source/util/ini.c +++ b/libraries/libstratosphere/source/util/ini.c @@ -70,7 +70,10 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(dest, src, size - 1); +#pragma GCC diagnostic pop dest[size - 1] = '\0'; return dest; } diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp index 73bbeacf3..86b3750b7 100644 --- a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp @@ -23,7 +23,7 @@ namespace ams::crypto { - template<size_t ModulusSize, typename Hash> /* requires HashFunction<Hash> */ + template<size_t ModulusSize, typename Hash> requires impl::HashFunction<Hash> class RsaOaepDecryptor { NON_COPYABLE(RsaOaepDecryptor); NON_MOVEABLE(RsaOaepDecryptor); diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp index bd29e4e2a..bcda34713 100644 --- a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp @@ -23,7 +23,7 @@ namespace ams::crypto { - template<size_t ModulusSize, typename Hash> /* requires HashFunction<Hash> */ + template<size_t ModulusSize, typename Hash> requires impl::HashFunction<Hash> class RsaOaepEncryptor { NON_COPYABLE(RsaOaepEncryptor); NON_MOVEABLE(RsaOaepEncryptor); diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_pss_verifier.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_pss_verifier.hpp index bd7ddcea6..273854489 100644 --- a/libraries/libvapours/include/vapours/crypto/crypto_rsa_pss_verifier.hpp +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_pss_verifier.hpp @@ -23,7 +23,7 @@ namespace ams::crypto { - template<size_t _ModulusSize, typename Hash> /* requires HashFunction<Hash> */ + template<size_t _ModulusSize, typename Hash> requires impl::HashFunction<Hash> class RsaPssVerifier { NON_COPYABLE(RsaPssVerifier); NON_MOVEABLE(RsaPssVerifier); diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_hash_function.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_hash_function.hpp index b598b46f6..68ca106a3 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_hash_function.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_hash_function.hpp @@ -23,15 +23,13 @@ namespace ams::crypto::impl { - /* TODO: C++20 - template<typename T> - concept HashFunction = requires(T &t, const void *cv, void *v, size_t sz) { - { T::HashSize } -> std::same_as<size_t>; - { T::BlockSize } -> std::same_as<size_t>; - { t.Initialize() } -> std::same_as<void>; - { t.Update(cv, sz) } -> std::same_as<void>; - { t.GetHash(v, sz) } -> std::same_as<void>; - }; - */ + template<typename T> + concept HashFunction = requires(T &t, const void *cv, void *v, size_t sz) { + { T::HashSize } -> std::convertible_to<size_t>; + { T::BlockSize } -> std::convertible_to<size_t>; + { t.Initialize() } -> std::same_as<void>; + { t.Update(cv, sz) } -> std::same_as<void>; + { t.GetHash(v, sz) } -> std::same_as<void>; + }; } diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp index d925f16a8..cc0e59563 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp @@ -22,7 +22,7 @@ namespace ams::crypto::impl { - template<typename Hash> /* requires HashFunction<Hash> */ + template<typename Hash> requires HashFunction<Hash> class RsaOaepImpl { NON_COPYABLE(RsaOaepImpl); NON_MOVEABLE(RsaOaepImpl); diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp index 1ce73e726..3febb5aba 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_pss_impl.hpp @@ -22,7 +22,7 @@ namespace ams::crypto::impl { - template<typename Hash> /* requires HashFunction<Hash> */ + template<typename Hash> requires HashFunction<Hash> class RsaPssImpl { NON_COPYABLE(RsaPssImpl); NON_MOVEABLE(RsaPssImpl); diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp index bbd145efb..ab9b69710 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_sha256_impl.hpp @@ -66,6 +66,6 @@ namespace ams::crypto::impl { } }; - /* static_assert(HashFunction<Sha256Impl>); */ + static_assert(HashFunction<Sha256Impl>); } diff --git a/libraries/libvapours/include/vapours/defines.hpp b/libraries/libvapours/include/vapours/defines.hpp index 5f78e90fc..391640806 100644 --- a/libraries/libvapours/include/vapours/defines.hpp +++ b/libraries/libvapours/include/vapours/defines.hpp @@ -35,9 +35,6 @@ #define CONST_FOLD(x) (__builtin_constant_p(x) ? (x) : (x)) -#define WRAP_TEMPLATE_CONSTANT(...) ([] { using U = union { static constexpr auto GetValue() { return __VA_ARGS__; } }; return U{}; }()) -#define UNWRAP_TEMPLATE_CONSTANT(tpnm) (tpnm::GetValue()) - #define CONCATENATE_IMPL(s1, s2) s1##s2 #define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) diff --git a/libraries/libvapours/include/vapours/includes.hpp b/libraries/libvapours/include/vapours/includes.hpp index 713d7d4c5..577e2c458 100644 --- a/libraries/libvapours/include/vapours/includes.hpp +++ b/libraries/libvapours/include/vapours/includes.hpp @@ -28,6 +28,7 @@ /* C++ headers. */ #include <type_traits> +#include <concepts> #include <algorithm> #include <iterator> #include <limits> @@ -38,6 +39,8 @@ #include <functional> #include <tuple> #include <array> +#include <bit> +#include <span> /* Stratosphere wants additional libstdc++ headers, others do not. */ #ifdef ATMOSPHERE_IS_STRATOSPHERE diff --git a/libraries/libvapours/include/vapours/span.hpp b/libraries/libvapours/include/vapours/span.hpp index ee0a07a2d..dc94f5f31 100644 --- a/libraries/libvapours/include/vapours/span.hpp +++ b/libraries/libvapours/include/vapours/span.hpp @@ -19,207 +19,7 @@ namespace ams { - /* TODO C++20 switch to template<typename T> using Span = std::span<T> */ - - namespace impl { - - template<typename Span> - class SpanConstIterator; - - template<typename Span, typename Derived, typename Reference> - class SpanIteratorImpl { - public: - friend class SpanConstIterator<Span>; - - using index_type = typename Span::index_type; - using difference_type = typename Span::difference_type; - using value_type = typename std::remove_cv<typename Span::element_type>::type; - using pointer = typename std::add_pointer<Reference>::type; - using reference = Reference; - using iterator_category = std::random_access_iterator_tag; - private: - const Span *span = nullptr; - index_type index = 0; - public: - constexpr ALWAYS_INLINE SpanIteratorImpl() = default; - constexpr ALWAYS_INLINE SpanIteratorImpl(const Span *s, index_type idx) : span(s), index(idx) { /* ... */ } - - constexpr ALWAYS_INLINE pointer operator->() const { - return this->span->data() + this->index; - } - - constexpr ALWAYS_INLINE reference operator*() const { - return *this->operator->(); - } - - constexpr ALWAYS_INLINE Derived operator++(int) { - auto prev = static_cast<Derived &>(*this); - ++(*this); - return prev; - } - - constexpr ALWAYS_INLINE Derived operator--(int) { - auto prev = static_cast<Derived &>(*this); - --(*this); - return prev; - } - - constexpr ALWAYS_INLINE Derived &operator++() { ++this->index; return static_cast<Derived &>(*this); } - constexpr ALWAYS_INLINE Derived &operator--() { --this->index; return static_cast<Derived &>(*this); } - - constexpr ALWAYS_INLINE Derived &operator+=(difference_type n) { this->index += n; return static_cast<Derived &>(*this); } - constexpr ALWAYS_INLINE Derived &operator-=(difference_type n) { this->index -= n; return static_cast<Derived &>(*this); } - - constexpr ALWAYS_INLINE Derived operator+(difference_type n) const { auto r = static_cast<const Derived &>(*this); return r += n; } - constexpr ALWAYS_INLINE Derived operator-(difference_type n) const { auto r = static_cast<const Derived &>(*this); return r -= n; } - - constexpr ALWAYS_INLINE friend Derived operator+(difference_type n, Derived it) { return it + n; } - constexpr ALWAYS_INLINE difference_type operator-(Derived rhs) const { AMS_ASSERT(this->span == rhs.span); return this->index - rhs.index; } - - constexpr ALWAYS_INLINE reference operator[](difference_type n) const { return *(*this + n); } - - constexpr ALWAYS_INLINE friend bool operator==(Derived lhs, Derived rhs) { - return lhs.span == rhs.span && lhs.index == rhs.index; - } - - constexpr ALWAYS_INLINE friend bool operator<(Derived lhs, Derived rhs) { - AMS_ASSERT(lhs.span == rhs.span); - return lhs.index < rhs.index; - } - - constexpr ALWAYS_INLINE friend bool operator!=(Derived lhs, Derived rhs) { return !(lhs == rhs); } - - constexpr ALWAYS_INLINE friend bool operator>(Derived lhs, Derived rhs) { return rhs < lhs; } - - constexpr ALWAYS_INLINE friend bool operator<=(Derived lhs, Derived rhs) { return !(lhs > rhs); } - constexpr ALWAYS_INLINE friend bool operator>=(Derived lhs, Derived rhs) { return !(lhs < rhs); } - }; - - template<typename Span> - class SpanIterator : public SpanIteratorImpl<Span, SpanIterator<Span>, typename Span::element_type&> { - public: - using SpanIteratorImpl<Span, SpanIterator<Span>, typename Span::element_type&>::SpanIteratorImpl; - }; - - template<typename Span> - class SpanConstIterator : public SpanIteratorImpl<Span, SpanConstIterator<Span>, const typename Span::element_type&> { - public: - using SpanIteratorImpl<Span, SpanConstIterator<Span>, const typename Span::element_type&>::SpanIteratorImpl; - - constexpr ALWAYS_INLINE SpanConstIterator() = default; - constexpr ALWAYS_INLINE SpanConstIterator(const SpanIterator<Span> &rhs) : SpanConstIterator(rhs.span, rhs.index) { /* ... */ } - }; - - } - template<typename T> - class Span { - public: - using element_type = T; - using value_type = typename std::remove_cv<element_type>::type; - using index_type = std::ptrdiff_t; - using difference_type = std::ptrdiff_t; - using pointer = element_type *; - using reference = element_type &; - using iterator = ::ams::impl::SpanIterator<Span>; - using const_iterator = ::ams::impl::SpanConstIterator<Span>; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; - private: - T *ptr; - index_type num_elements; - public: - constexpr ALWAYS_INLINE Span() : ptr(), num_elements() { /* ... */ } - - constexpr ALWAYS_INLINE Span(T *p, index_type size) : ptr(p), num_elements(size) { - AMS_ASSERT(this->num_elements > 0 || this->ptr == nullptr); - } - - constexpr ALWAYS_INLINE Span(T *start, T *end) : Span(start, end - start) { /* ... */ } - - template<size_t Size> - constexpr ALWAYS_INLINE Span(T (&arr)[Size]) : Span(static_cast<T *>(arr), static_cast<index_type>(Size)) { /* ... */ } - - template<size_t Size> - constexpr ALWAYS_INLINE Span(std::array<value_type, Size> &arr) : Span(arr.data(), static_cast<index_type>(Size)) { /* ... */ } - - template<size_t Size> - constexpr ALWAYS_INLINE Span(const std::array<value_type, Size> &arr) : Span(arr.data(), static_cast<index_type>(Size)) { /* ... */ } - - template<typename U, typename = typename std::enable_if<std::is_convertible<U(*)[], T(*)[]>::value>::type> - constexpr ALWAYS_INLINE Span(const Span<U> &rhs) : Span(rhs.data(), rhs.size()) { /* ... */ } - public: - constexpr ALWAYS_INLINE iterator begin() const { return { this, 0 }; } - constexpr ALWAYS_INLINE iterator end() const { return { this, this->num_elements }; } - - constexpr ALWAYS_INLINE const_iterator cbegin() const { return { this, 0 }; } - constexpr ALWAYS_INLINE const_iterator cend() const { return { this, this->num_elements }; } - - constexpr ALWAYS_INLINE reverse_iterator rbegin() const { return reverse_iterator(this->end()); } - constexpr ALWAYS_INLINE reverse_iterator rend() const { return reverse_iterator(this->begin()); } - - constexpr ALWAYS_INLINE const_reverse_iterator crbegin() const { return reverse_iterator(this->cend()); } - constexpr ALWAYS_INLINE const_reverse_iterator crend() const { return reverse_iterator(this->cbegin()); } - - constexpr ALWAYS_INLINE pointer data() const { return this->ptr; } - - constexpr ALWAYS_INLINE index_type size() const { return this->num_elements; } - constexpr ALWAYS_INLINE index_type size_bytes() const { return this->size() * sizeof(T); } - - constexpr ALWAYS_INLINE bool empty() const { return this->size() == 0; } - - constexpr ALWAYS_INLINE T &operator[](index_type idx) const { - AMS_ASSERT(idx < this->size()); - return this->ptr[idx]; - } - - constexpr ALWAYS_INLINE T &operator()(index_type idx) const { return (*this)[idx]; } - - constexpr ALWAYS_INLINE Span first(index_type size) const { - AMS_ASSERT(size <= this->size()); - return { this->ptr, size }; - } - - constexpr ALWAYS_INLINE Span last(index_type size) const { - AMS_ASSERT(size <= this->size()); - return { this->ptr + (this->size() - size), size }; - } - - constexpr ALWAYS_INLINE Span subspan(index_type idx, index_type size) const { - AMS_ASSERT(size <= this->size()); - AMS_ASSERT(this->size() - size >= idx); - return { this->ptr + idx, size }; - } - - constexpr ALWAYS_INLINE Span subspan(index_type idx) const { - AMS_ASSERT(idx <= this->size()); - return { this->ptr + idx, this->size() - idx }; - } - }; - - template<typename T> - constexpr ALWAYS_INLINE Span<T> MakeSpan(T *start, T *end) { - return { start, end }; - } - - template<typename T> - constexpr ALWAYS_INLINE Span<T> MakeSpan(T *p, typename Span<T>::index_type size) { - return { p, size }; - } - - template<typename T, size_t Size> - constexpr ALWAYS_INLINE Span<T> MakeSpan(T (&arr)[Size]) { - return Span<T>(arr); - } - - template<typename T, size_t Size> - constexpr ALWAYS_INLINE Span<T> MakeSpan(std::array<T, Size> &arr) { - return Span<T>(arr); - } - - template<typename T, size_t Size> - constexpr ALWAYS_INLINE Span<const T> MakeSpan(const std::array<T, Size> &arr) { - return Span<const T>(arr); - } + using Span = std::span<T>; } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp index 3cf0b73d4..88866a051 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_code_generator.hpp @@ -261,12 +261,11 @@ namespace ams::svc::codegen::impl { /* TODO */ }; - template<typename CodeGenerator, typename MetaCodeHolder> - static ALWAYS_INLINE void GenerateCodeForMetaCode(MetaCodeHolder) { - constexpr auto MetaCode = UNWRAP_TEMPLATE_CONSTANT(MetaCodeHolder); + template<typename CodeGenerator, auto MetaCode> + static ALWAYS_INLINE void GenerateCodeForMetaCode() { constexpr size_t NumOperations = MetaCode.GetNumOperations(); static_assert(NumOperations <= 64); - #define SVC_CODEGEN_HANDLER(n) do { if constexpr (n < NumOperations) { constexpr auto Operation = MetaCode.GetOperation(n); GenerateCodeForOperation<CodeGenerator>(WRAP_TEMPLATE_CONSTANT(Operation)); } } while (0) + #define SVC_CODEGEN_HANDLER(n) do { if constexpr (n < NumOperations) { constexpr auto Operation = MetaCode.GetOperation(n); GenerateCodeForOperation<CodeGenerator, Operation>(); } } while (0) SVC_CODEGEN_FOR_I_FROM_0_TO_64(SVC_CODEGEN_HANDLER) #undef SVC_CODEGEN_HANDLER } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_common.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_common.hpp index 97f6f5d22..f1a7b2b63 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_common.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_common.hpp @@ -143,7 +143,7 @@ namespace ams::svc::codegen::impl { template<size_t N> class RegisterAllocator { - private: + public: std::array<bool, N> map; public: constexpr explicit RegisterAllocator() : map() { /* ... */ } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp index 44f11b441..6872199b1 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp @@ -319,38 +319,43 @@ namespace ams::svc::codegen::impl { template<typename... T> struct TypeIndexFilter { - template<typename UseArrayHolder, typename HeadType, typename... TailType, size_t HeadIndex, size_t... TailIndex> - static constexpr auto GetFilteredTupleImpl(UseArrayHolder, std::tuple<HeadType, TailType...>, std::index_sequence<HeadIndex, TailIndex...>) { - constexpr auto UseArray = UNWRAP_TEMPLATE_CONSTANT(UseArrayHolder); - static_assert(sizeof...(TailType) == sizeof...(TailIndex)); - static_assert(HeadIndex <= UseArray.size()); - if constexpr (sizeof...(TailType) == 0) { - if constexpr (!UseArray[HeadIndex]) { - return std::tuple<HeadType>{}; - } else { - return std::tuple<>{}; - } - } else { - auto tail_tuple = GetFilteredTupleImpl(UseArrayHolder{}, std::tuple<TailType...>{}, std::index_sequence<TailIndex...>{}); - if constexpr (!UseArray[HeadIndex]) { - return std::tuple_cat(std::tuple<HeadType>{}, tail_tuple); - } else { - return std::tuple_cat(std::tuple<>{}, tail_tuple); - } - } - } + template<auto UseArray, typename X, typename Y> + struct Helper; - template<typename UseArrayHolder> - static constexpr auto GetFilteredTuple(UseArrayHolder) { - return GetFilteredTupleImpl(UseArrayHolder{}, std::tuple<T...>{}, std::make_index_sequence<sizeof...(T)>()); - } + template<auto UseArray, size_t...Index> + struct Helper<UseArray, std::tuple<>, std::index_sequence<Index...>> { + using Type = std::tuple<>; + }; + + template<auto UseArray, typename HeadType, typename... TailType, size_t HeadIndex, size_t... TailIndex> + struct Helper<UseArray, std::tuple<HeadType, TailType...>, std::index_sequence<HeadIndex, TailIndex...>> { + + using LastHeadType = std::tuple<HeadType>; + using LastNullType = std::tuple<>; + + using LastType = typename std::conditional<!UseArray[HeadIndex], LastHeadType, LastNullType>::type; + + using NextTailType = std::tuple<TailType...>; + using NextTailSequence = std::index_sequence<TailIndex...>; + + using NextType = typename std::conditional<!UseArray[HeadIndex], + decltype(std::tuple_cat(std::declval<LastHeadType>(), std::declval<typename Helper<UseArray, NextTailType, NextTailSequence>::Type>())), + decltype(std::tuple_cat(std::declval<LastNullType>(), std::declval<typename Helper<UseArray, NextTailType, NextTailSequence>::Type>())) + >::type; + + using Type = typename std::conditional<sizeof...(TailType) == 0, LastType, NextType>::type; + + }; + + template<auto UseArray> + using FilteredTupleType = typename Helper<UseArray, std::tuple<T...>, decltype(std::make_index_sequence<sizeof...(T)>())>::Type; }; - template<typename AllocatorHolder, typename FirstOperation, typename...OtherOperations> - static constexpr auto GetModifiedOperations(AllocatorHolder, std::tuple<FirstOperation, OtherOperations...> ops) { + template<auto Allocator, typename FirstOperation, typename...OtherOperations> + static constexpr auto GetModifiedOperations(std::tuple<FirstOperation, OtherOperations...> ops) { constexpr size_t ModifyRegister = [] { - auto allocator = UNWRAP_TEMPLATE_CONSTANT(AllocatorHolder); + auto allocator = Allocator; return allocator.AllocateFirstFree(); }(); @@ -359,13 +364,13 @@ namespace ams::svc::codegen::impl { return std::tuple<ModifiedFirstOperation, OtherOperations..., NewMoveOperation>{}; } - template<typename Conversion, typename AllocatorHolder, typename FirstOperation, typename... OtherOperations> - static constexpr auto GenerateBeforeOperations(MetaCodeGenerator &mcg, AllocatorHolder, std::tuple<FirstOperation, OtherOperations...> ops) -> RegisterAllocator<UNWRAP_TEMPLATE_CONSTANT(AllocatorHolder).GetRegisterCount()> { + template<typename Conversion, auto Allocator, typename FirstOperation, typename... OtherOperations> + static constexpr auto GenerateBeforeOperations(MetaCodeGenerator &mcg, std::tuple<FirstOperation, OtherOperations...> ops) -> RegisterAllocator<Allocator.GetRegisterCount()> { constexpr size_t NumOperations = 1 + sizeof...(OtherOperations); using OperationsTuple = decltype(ops); using FilterHelper = TypeIndexFilter<FirstOperation, OtherOperations...>; - constexpr auto ProcessOperation = []<typename Operation>(MetaCodeGenerator &pr_mcg, auto &allocator, Operation) { + constexpr auto ProcessOperation = []<typename Operation>(MetaCodeGenerator &pr_mcg, auto &allocator) { if (Conversion::template CanGenerateCode<Operation, CodeGenerationKind::SvcInvocationToKernelProcedure>(allocator)) { Conversion::template GenerateCode<Operation, CodeGenerationKind::SvcInvocationToKernelProcedure>(pr_mcg, allocator); return true; @@ -373,12 +378,12 @@ namespace ams::svc::codegen::impl { return false; }; - constexpr auto ProcessResults = [ProcessOperation]<typename... Operations>(std::tuple<Operations...>) { - auto allocator = UNWRAP_TEMPLATE_CONSTANT(AllocatorHolder); + constexpr auto ProcessResults = []<auto AllocatorVal, auto ProcessOp, typename... Operations>(std::tuple<Operations...>) { + auto allocator = AllocatorVal; MetaCodeGenerator pr_mcg; - auto use_array = std::array<bool, NumOperations>{ ProcessOperation(pr_mcg, allocator, Operations{})... }; + auto use_array = std::array<bool, NumOperations>{ ProcessOp.template operator()<Operations>(pr_mcg, allocator)... }; return std::make_tuple(use_array, allocator, pr_mcg); - }(OperationsTuple{}); + }.template operator()<Allocator, ProcessOperation>(OperationsTuple{}); constexpr auto CanGenerate = std::get<0>(ProcessResults); constexpr auto AfterAllocator = std::get<1>(ProcessResults); @@ -388,15 +393,15 @@ namespace ams::svc::codegen::impl { mcg.AddOperationDirectly(GeneratedCode.GetOperation(i)); } - constexpr auto FilteredOperations = FilterHelper::template GetFilteredTuple(WRAP_TEMPLATE_CONSTANT(CanGenerate)); - static_assert(std::tuple_size<decltype(FilteredOperations)>::value <= NumOperations); - if constexpr (std::tuple_size<decltype(FilteredOperations)>::value > 0) { - if constexpr (std::tuple_size<decltype(FilteredOperations)>::value != NumOperations) { - return GenerateBeforeOperations<Conversion>(mcg, WRAP_TEMPLATE_CONSTANT(AfterAllocator), FilteredOperations); + using FilteredOperations = typename FilterHelper::FilteredTupleType<CanGenerate>; + static_assert(std::tuple_size<FilteredOperations>::value <= NumOperations); + if constexpr (std::tuple_size<FilteredOperations>::value > 0) { + if constexpr (std::tuple_size<FilteredOperations>::value != NumOperations) { + return GenerateBeforeOperations<Conversion, AfterAllocator>(mcg, FilteredOperations{}); } else { /* No progress was made, so we need to make a change. */ - constexpr auto ModifiedOperations = GetModifiedOperations(WRAP_TEMPLATE_CONSTANT(AfterAllocator), FilteredOperations); - return GenerateBeforeOperations<Conversion>(mcg, WRAP_TEMPLATE_CONSTANT(AfterAllocator), ModifiedOperations); + constexpr auto ModifiedOperations = GetModifiedOperations<AfterAllocator>(FilteredOperations{}); + return GenerateBeforeOperations<Conversion, AfterAllocator>(mcg, ModifiedOperations); } } else { return AfterAllocator; @@ -433,7 +438,7 @@ namespace ams::svc::codegen::impl { /* Generate code for before operations. */ if constexpr (Conversion::NumBeforeOperations > 0) { - allocator = GenerateBeforeOperations<Conversion>(mcg, WRAP_TEMPLATE_CONSTANT(InitialAllocator), typename Conversion::BeforeOperations{}); + allocator = GenerateBeforeOperations<Conversion, InitialAllocator>(mcg, typename Conversion::BeforeOperations{}); } else { allocator = InitialAllocator; } @@ -527,8 +532,8 @@ namespace ams::svc::codegen::impl { static ALWAYS_INLINE void WrapSvcFunction() { /* Generate appropriate assembly. */ - GenerateCodeForMetaCode<CodeGenerator>(WRAP_TEMPLATE_CONSTANT(BeforeMetaCode)); - ON_SCOPE_EXIT { GenerateCodeForMetaCode<CodeGenerator>(WRAP_TEMPLATE_CONSTANT(AfterMetaCode)); }; + GenerateCodeForMetaCode<CodeGenerator, BeforeMetaCode>(); + ON_SCOPE_EXIT { GenerateCodeForMetaCode<CodeGenerator, AfterMetaCode>(); }; return reinterpret_cast<void (*)()>(Function)(); } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp index 1f4eb0f0c..4a6adadd8 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout.hpp @@ -24,7 +24,7 @@ namespace ams::svc::codegen::impl { static constexpr size_t MaxParameters = 8; private: static constexpr size_t InvalidIndex = std::numeric_limits<size_t>::max(); - private: + public: /* ABI parameters. */ Abi abi; @@ -102,7 +102,7 @@ namespace ams::svc::codegen::impl { }; class ProcedureLayout { - private: + public: Abi abi; ParameterLayout input; ParameterLayout output; @@ -205,7 +205,7 @@ namespace ams::svc::codegen::impl { }; class SvcInvocationLayout { - private: + public: Abi abi; ParameterLayout input; ParameterLayout output; @@ -220,11 +220,7 @@ namespace ams::svc::codegen::impl { } for (size_t i = 1; i < num_parameters; i++) { for (size_t j = i; j > 0 && param_layout.GetParameter(map[j-1]).GetLocation(0) > param_layout.GetParameter(map[j]).GetLocation(0); j--) { - /* std::swap is not constexpr until c++20 :( */ - /* TODO: std::swap(map[j], map[j-1]); */ - const size_t tmp = map[j]; - map[j] = map[j-1]; - map[j-1] = tmp; + std::swap(map[j], map[j-1]); } } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp index bd0190c50..1b0189925 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp @@ -304,9 +304,8 @@ namespace ams::svc::codegen::impl { static constexpr auto DetermineConversionOperations() { [[maybe_unused]] constexpr auto Procedure = LayoutForKernel; [[maybe_unused]] constexpr ParameterLayout Svc = Input ? LayoutForSvc.GetInputLayout() : LayoutForSvc.GetOutputLayout(); - [[maybe_unused]] constexpr std::array<size_t, Svc.GetNumParameters()> ParameterMap = []<typename SvcHolder>(SvcHolder){ + [[maybe_unused]] constexpr std::array<size_t, Svc.GetNumParameters()> ParameterMap = []<auto CapturedSvc>(){ /* We want to iterate over the parameters in sorted order. */ - constexpr ParameterLayout CapturedSvc = UNWRAP_TEMPLATE_CONSTANT(SvcHolder); std::array<size_t, CapturedSvc.GetNumParameters()> map{}; const size_t num_parameters = CapturedSvc.GetNumParameters(); for (size_t i = 0; i < num_parameters; i++) { @@ -314,15 +313,11 @@ namespace ams::svc::codegen::impl { } for (size_t i = 1; i < num_parameters; i++) { for (size_t j = i; j > 0 && CapturedSvc.GetParameter(map[j-1]).GetLocation(0) > CapturedSvc.GetParameter(map[j]).GetLocation(0); j--) { - /* std::swap is not constexpr until c++20 :( */ - /* TODO: std::swap(map[j], map[j-1]); */ - const size_t tmp = map[j]; - map[j] = map[j-1]; - map[j-1] = tmp; + std::swap(map[j], map[j-1]); } } return map; - }(WRAP_TEMPLATE_CONSTANT(Svc)); + }.template operator()<Svc>(); if constexpr (ParameterIndex >= Svc.GetNumParameters()) { /* Base case: we're done. */ @@ -377,16 +372,13 @@ namespace ams::svc::codegen::impl { constexpr size_t RegisterSize = SvcAbiType::RegisterSize; constexpr size_t PassedSize = ProcedureParam.GetTypeSize(); - /* TODO: C++20 templated lambdas. For now, use GCC extension syntax. */ - constexpr auto SvcIndexSequence = []<typename SvcParamWrapper, size_t... Is>(SvcParamWrapper, std::index_sequence<Is...>) { - constexpr Parameter CapturedSvcParam = UNWRAP_TEMPLATE_CONSTANT(SvcParamWrapper); + constexpr auto SvcIndexSequence = []<auto CapturedSvcParam, size_t... Is>(std::index_sequence<Is...>) { return std::index_sequence<CapturedSvcParam.GetLocation(Is).GetIndex()...>{}; - }(WRAP_TEMPLATE_CONSTANT(SvcParam), std::make_index_sequence<SvcParam.GetNumLocations()>()); + }.template operator()<SvcParam>(std::make_index_sequence<SvcParam.GetNumLocations()>()); - constexpr auto OperationValue = []<typename ProcedureLocWrapper, size_t... Is>(ProcedureLocWrapper, std::index_sequence<Is...>) { - constexpr Location CapturedProcedureLoc = UNWRAP_TEMPLATE_CONSTANT(ProcedureLocWrapper); + constexpr auto OperationValue = []<auto CapturedProcedureLoc, size_t... Is>(std::index_sequence<Is...>) { return LayoutConversionBase::OperationScatter<RegisterSize, PassedSize, StackIndex * KernelAbiType::RegisterSize, CapturedProcedureLoc.GetIndex(), Is...>{}; - }(WRAP_TEMPLATE_CONSTANT(ProcedureLoc), SvcIndexSequence); + }.template operator()<ProcedureLoc>(SvcIndexSequence); constexpr auto cur_op = std::make_tuple(OperationValue); diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp index 4b1b5d27f..8d84a6182 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_meta_code.hpp @@ -79,7 +79,7 @@ namespace ams::svc::codegen::impl { return op; } - private: + public: size_t num_operations; std::array<Operation, MaxOperations> operations; public: @@ -98,15 +98,13 @@ namespace ams::svc::codegen::impl { } }; - template<typename _OperationHolder> + template<auto Operation> static constexpr auto GetOperationParameterSequence() { - constexpr auto _Operation = UNWRAP_TEMPLATE_CONSTANT(_OperationHolder); - constexpr size_t NumParameters = _Operation.num_parameters; + constexpr size_t NumParameters = Operation.num_parameters; - return []<typename OperationHolder, size_t... Is>(OperationHolder, std::index_sequence<Is...>) { - constexpr auto Operation = UNWRAP_TEMPLATE_CONSTANT(OperationHolder); + return []<size_t... Is>(std::index_sequence<Is...>) { return std::index_sequence<Operation.parameters[Is]...>{}; - }(_OperationHolder{}, std::make_index_sequence<NumParameters>()); + }(std::make_index_sequence<NumParameters>()); } template<typename CodeGenerator, MetaCode::OperationKind Kind, size_t... Parameters> @@ -130,10 +128,9 @@ namespace ams::svc::codegen::impl { #undef META_CODE_OPERATION_KIND_GENERATE_CODE } - template<typename CodeGenerator, typename OperationHolder> - static ALWAYS_INLINE void GenerateCodeForOperation(OperationHolder) { - constexpr auto Operation = UNWRAP_TEMPLATE_CONSTANT(OperationHolder); - GenerateCodeForOperationImpl<CodeGenerator, Operation.kind>(GetOperationParameterSequence<OperationHolder>()); + template<typename CodeGenerator, auto Operation> + static ALWAYS_INLINE void GenerateCodeForOperation() { + GenerateCodeForOperationImpl<CodeGenerator, Operation.kind>(GetOperationParameterSequence<Operation>()); } class MetaCodeGenerator { diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp index 76825f3e4..f077e30ba 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_parameter.hpp @@ -27,7 +27,7 @@ namespace ams::svc::codegen::impl { class Location { private: static constexpr size_t InvalidIndex = std::numeric_limits<size_t>::max(); - private: + public: Storage storage; size_t index; public: @@ -75,7 +75,7 @@ namespace ams::svc::codegen::impl { static constexpr size_t MaxLocations = 8; static constexpr size_t IdentifierLengthMax = 0x40; class Identifier { - private: + public: char name[IdentifierLengthMax]; size_t index; public: @@ -99,7 +99,7 @@ namespace ams::svc::codegen::impl { return !(*this == rhs); } }; - private: + public: Identifier identifier; ArgumentType type; size_t type_size; diff --git a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp index 9a979e874..1c379ca74 100644 --- a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp +++ b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp @@ -58,11 +58,11 @@ namespace ams::svc::ipc { private: util::BitPack32 header[2]; public: - constexpr ALWAYS_INLINE MessageHeader() : header({util::BitPack32{0}, util::BitPack32{0}}) { + constexpr ALWAYS_INLINE MessageHeader() : header{util::BitPack32{0}, util::BitPack32{0}} { this->header[0].Set<Tag>(NullTag); } - constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : header({util::BitPack32{0}, util::BitPack32{0}}) { + constexpr ALWAYS_INLINE MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, s32 raw, s32 recv_list) : header{util::BitPack32{0}, util::BitPack32{0}} { this->header[0].Set<Tag>(tag); this->header[0].Set<PointerCount>(ptr); this->header[0].Set<SendCount>(send); @@ -74,11 +74,11 @@ namespace ams::svc::ipc { this->header[1].Set<HasSpecialHeader>(special); } - ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : header({util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE explicit MessageHeader(const MessageBuffer &buf) : header{util::BitPack32{0}, util::BitPack32{0}} { buf.Get(0, this->header, util::size(this->header)); } - ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : header({util::BitPack32{msg[0]}, util::BitPack32{msg[1]}}) { /* ... */ } + ALWAYS_INLINE explicit MessageHeader(const u32 *msg) : header{util::BitPack32{msg[0]}, util::BitPack32{msg[1]}} { /* ... */ } constexpr ALWAYS_INLINE u16 GetTag() const { return this->header[0].Get<Tag>(); @@ -219,9 +219,9 @@ namespace ams::svc::ipc { private: util::BitPack32 data[3]; public: - constexpr ALWAYS_INLINE MapAliasDescriptor() : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ } + constexpr ALWAYS_INLINE MapAliasDescriptor() : data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} { /* ... */ } - ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE MapAliasDescriptor(const void *buffer, size_t _size, Attribute attr = Attribute_Ipc) : data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} { const u64 address = reinterpret_cast<u64>(buffer); const u64 size = static_cast<u64>(_size); this->data[0] = { static_cast<u32>(size) }; @@ -233,7 +233,7 @@ namespace ams::svc::ipc { this->data[2].Set<AddressHigh>(GetAddressHigh(address)); } - ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE MapAliasDescriptor(const MessageBuffer &buf, s32 index) : data{util::BitPack32{0}, util::BitPack32{0}, util::BitPack32{0}} { buf.Get(index, this->data, util::size(this->data)); } @@ -283,9 +283,9 @@ namespace ams::svc::ipc { private: util::BitPack32 data[2]; public: - constexpr ALWAYS_INLINE PointerDescriptor() : data({util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ } + constexpr ALWAYS_INLINE PointerDescriptor() : data{util::BitPack32{0}, util::BitPack32{0}} { /* ... */ } - ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE PointerDescriptor(const void *buffer, size_t size, s32 index) : data{util::BitPack32{0}, util::BitPack32{0}} { const u64 address = reinterpret_cast<u64>(buffer); this->data[0].Set<Index>(index); @@ -296,7 +296,7 @@ namespace ams::svc::ipc { this->data[1] = { static_cast<u32>(address) }; } - ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : data({util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE PointerDescriptor(const MessageBuffer &buf, s32 index) : data{util::BitPack32{0}, util::BitPack32{0}} { buf.Get(index, this->data, util::size(this->data)); } @@ -338,9 +338,9 @@ namespace ams::svc::ipc { private: util::BitPack32 data[2]; public: - constexpr ALWAYS_INLINE ReceiveListEntry() : data({util::BitPack32{0}, util::BitPack32{0}}) { /* ... */ } + constexpr ALWAYS_INLINE ReceiveListEntry() : data{util::BitPack32{0}, util::BitPack32{0}} { /* ... */ } - ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : data({util::BitPack32{0}, util::BitPack32{0}}) { + ALWAYS_INLINE ReceiveListEntry(const void *buffer, size_t size) : data{util::BitPack32{0}, util::BitPack32{0}} { const u64 address = reinterpret_cast<u64>(buffer); this->data[0] = { static_cast<u32>(address) }; @@ -349,7 +349,7 @@ namespace ams::svc::ipc { this->data[1].Set<Size>(size); } - ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data({util::BitPack32{a}, util::BitPack32{b}}) { /* ... */ } + ALWAYS_INLINE ReceiveListEntry(u32 a, u32 b) : data{util::BitPack32{a}, util::BitPack32{b}} { /* ... */ } constexpr ALWAYS_INLINE uintptr_t GetAddress() { const u64 address = (static_cast<u64>(this->data[1].Get<AddressHigh>()) << AddressLow::Count) | this->data[0].Get<AddressLow>(); diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 3048f515a..867668f83 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -18,6 +18,7 @@ #include <vapours/common.hpp> #include <vapours/assert.hpp> +#include <vapours/util/util_type_traits.hpp> #include <vapours/util/util_alignment.hpp> #include <vapours/util/util_size.hpp> #include <vapours/util/util_endian.hpp> diff --git a/libraries/libvapours/include/vapours/util/util_bitpack.hpp b/libraries/libvapours/include/vapours/util/util_bitpack.hpp index fac3c681a..c89058d61 100644 --- a/libraries/libvapours/include/vapours/util/util_bitpack.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitpack.hpp @@ -50,7 +50,7 @@ namespace ams::util { static constexpr size_t Next = Index + Count; using BitPackType = BitPack<IntegralStorageType>; - static_assert(std::is_pod<BitPackType>::value); + static_assert(util::is_pod<BitPackType>::value); static_assert(Mask<Index, Count> != 0); static_assert(std::is_integral<T>::value || std::is_enum<T>::value); @@ -84,10 +84,10 @@ namespace ams::util { using BitPack32 = impl::BitPack<u32>; using BitPack64 = impl::BitPack<u64>; - static_assert(std::is_pod<BitPack8>::value); - static_assert(std::is_pod<BitPack16>::value); - static_assert(std::is_pod<BitPack32>::value); - static_assert(std::is_pod<BitPack64>::value); + static_assert(util::is_pod<BitPack8>::value); + static_assert(util::is_pod<BitPack16>::value); + static_assert(util::is_pod<BitPack32>::value); + static_assert(util::is_pod<BitPack64>::value); static_assert(std::is_trivially_destructible<BitPack8 >::value); static_assert(std::is_trivially_destructible<BitPack16>::value); static_assert(std::is_trivially_destructible<BitPack32>::value); diff --git a/libraries/libvapours/include/vapours/util/util_bitutil.hpp b/libraries/libvapours/include/vapours/util/util_bitutil.hpp index de8e78983..cf974f62d 100644 --- a/libraries/libvapours/include/vapours/util/util_bitutil.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitutil.hpp @@ -30,11 +30,9 @@ namespace ams::util { } - template <typename T> + template <typename T> requires std::integral<T> class BitsOf { private: - static_assert(std::is_integral<T>::value); - static constexpr ALWAYS_INLINE int GetLsbPos(T v) { return __builtin_ctzll(static_cast<u64>(v)); } @@ -78,69 +76,68 @@ namespace ams::util { } }; - template<typename T = u64, typename ...Args> + template<typename T = u64, typename ...Args> requires std::integral<T> constexpr ALWAYS_INLINE T CombineBits(Args... args) { return (... | (T(1u) << args)); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ResetLeastSignificantOneBit(T x) { return x & (x - 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T SetLeastSignificantZeroBit(T x) { return x | (x + 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LeastSignificantOneBit(T x) { return x & ~(x - 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LeastSignificantZeroBit(T x) { return ~x & (x + 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ResetTrailingOnes(T x) { return x & (x + 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T SetTrailingZeros(T x) { return x | (x - 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T MaskTrailingZeros(T x) { return (~x) & (x - 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T MaskTrailingOnes(T x) { return ~((~x) | (x + 1)); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T MaskTrailingZerosAndLeastSignificantOneBit(T x) { return x ^ (x - 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T MaskTrailingOnesAndLeastSignificantZeroBit(T x) { return x ^ (x + 1); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE int PopCount(T x) { /* TODO: C++20 std::bit_cast */ using U = typename std::make_unsigned<T>::type; U u = static_cast<U>(x); - /* TODO: C++20 std::is_constant_evaluated */ - if (false) { + if (std::is_constant_evaluated()) { /* https://en.wikipedia.org/wiki/Hamming_weight */ constexpr U m1 = U(-1) / 0x03; constexpr U m2 = U(-1) / 0x05; @@ -168,10 +165,9 @@ namespace ams::util { } } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE int CountLeadingZeros(T x) { - /* TODO: C++20 std::is_constant_evaluated */ - if (false) { + if (std::is_constant_evaluated()) { for (size_t i = 0; i < impl::Log2<BITSIZEOF(T)>; ++i) { const size_t shift = (0x1 << i); x |= x >> shift; @@ -195,18 +191,18 @@ namespace ams::util { } } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE bool IsPowerOfTwo(T x) { return x > 0 && ResetLeastSignificantOneBit(x) == 0; } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T CeilingPowerOfTwo(T x) { AMS_ASSERT(x > 0); return T(1) << (BITSIZEOF(T) - CountLeadingZeros(T(x - 1))); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T FloorPowerOfTwo(T x) { AMS_ASSERT(x > 0); return T(1) << (BITSIZEOF(T) - CountLeadingZeros(x) - 1); diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp index fcf294baf..19844f202 100644 --- a/libraries/libvapours/include/vapours/util/util_endian.hpp +++ b/libraries/libvapours/include/vapours/util/util_endian.hpp @@ -20,27 +20,17 @@ namespace ams::util { - /* TODO: C++20 std::endian */ - constexpr bool IsLittleEndian() { - #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - return true; - #else - return false; - #endif + return std::endian::native == std::endian::little; } constexpr bool IsBigEndian() { - #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - return true; - #else - return false; - #endif + return std::endian::native == std::endian::big; } static_assert(IsLittleEndian() ^ IsBigEndian()); - template<typename U> /* requires unsigned_integral<U> */ + template<typename U> requires std::unsigned_integral<U> constexpr ALWAYS_INLINE U SwapBytes(const U u) { static_assert(BITSIZEOF(u8) == 8); constexpr U ByteMask = 0xFFu; @@ -85,14 +75,14 @@ namespace ams::util { ((u & (ByteMask << 0)) << 40); } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE void SwapBytes(T *ptr) { using U = typename std::make_unsigned<T>::type; *ptr = static_cast<T>(SwapBytes(static_cast<U>(*ptr))); } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ConvertToBigEndian(const T val) { using U = typename std::make_unsigned<T>::type; @@ -104,7 +94,7 @@ namespace ams::util { } } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ConvertToLittleEndian(const T val) { using U = typename std::make_unsigned<T>::type; @@ -116,7 +106,7 @@ namespace ams::util { } } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ConvertToBigEndian48(const T val) { using U = typename std::make_unsigned<T>::type; static_assert(sizeof(T) == sizeof(u64)); @@ -130,7 +120,7 @@ namespace ams::util { } } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T ConvertToLittleEndian48(const T val) { using U = typename std::make_unsigned<T>::type; static_assert(sizeof(T) == sizeof(u64)); @@ -144,12 +134,12 @@ namespace ams::util { } } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LoadBigEndian(T *ptr) { return ConvertToBigEndian(*ptr); } - template<typename T> /* requires integral<T> */ + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LoadLittleEndian(T *ptr) { return ConvertToLittleEndian(*ptr); } diff --git a/libraries/libvapours/include/vapours/util/util_fourcc.hpp b/libraries/libvapours/include/vapours/util/util_fourcc.hpp index 5b805e03b..08e8b3c08 100644 --- a/libraries/libvapours/include/vapours/util/util_fourcc.hpp +++ b/libraries/libvapours/include/vapours/util/util_fourcc.hpp @@ -17,16 +17,14 @@ #pragma once #include <vapours/common.hpp> #include <vapours/assert.hpp> +#include <vapours/util/util_endian.hpp> namespace ams::util { template<char A, char B, char C, char D> struct FourCC { - /* TODO: C++20 std::endian */ - static constexpr u32 Code = (static_cast<u32>(A) << 0x00) | - (static_cast<u32>(B) << 0x08) | - (static_cast<u32>(C) << 0x10) | - (static_cast<u32>(D) << 0x18); + static constexpr u32 Code = IsLittleEndian() ? ((static_cast<u32>(A) << 0x00) | (static_cast<u32>(B) << 0x08) | (static_cast<u32>(C) << 0x10) | (static_cast<u32>(D) << 0x18)) + : ((static_cast<u32>(A) << 0x18) | (static_cast<u32>(B) << 0x10) | (static_cast<u32>(C) << 0x08) | (static_cast<u32>(D) << 0x00)); static constexpr const char String[] = {A, B, C, D}; @@ -36,11 +34,8 @@ namespace ams::util { template<char A, char B, char C, char D> struct ReverseFourCC { - /* TODO: C++20 std::endian */ - static constexpr u32 Code = (static_cast<u32>(A) << 0x18) | - (static_cast<u32>(B) << 0x10) | - (static_cast<u32>(C) << 0x08) | - (static_cast<u32>(D) << 0x00); + static constexpr u32 Code = IsLittleEndian() ? ((static_cast<u32>(A) << 0x18) | (static_cast<u32>(B) << 0x10) | (static_cast<u32>(C) << 0x08) | (static_cast<u32>(D) << 0x00)) + : ((static_cast<u32>(A) << 0x00) | (static_cast<u32>(B) << 0x08) | (static_cast<u32>(C) << 0x10) | (static_cast<u32>(D) << 0x18)); static constexpr const char String[] = {D, C, B, A}; diff --git a/libraries/libvapours/include/vapours/util/util_type_traits.hpp b/libraries/libvapours/include/vapours/util/util_type_traits.hpp new file mode 100644 index 000000000..146b41a46 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_type_traits.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> + +namespace ams::util { + + template<typename T> + using is_pod = std::bool_constant<std::is_standard_layout<T>::value && std::is_trivial<T>::value>; + +} diff --git a/mesosphere/kernel/source/kern_kernel_instantiations.cpp b/mesosphere/kernel/source/kern_kernel_instantiations.cpp index 743dc5596..5bed676ec 100644 --- a/mesosphere/kernel/source/kern_kernel_instantiations.cpp +++ b/mesosphere/kernel/source/kern_kernel_instantiations.cpp @@ -18,22 +18,22 @@ namespace ams::kern { /* Declare kernel data members in kernel TU. */ - Kernel::State Kernel::s_state = Kernel::State::Invalid; - KResourceLimit Kernel::s_system_resource_limit; - KMemoryManager Kernel::s_memory_manager; - KPageTableManager Kernel::s_page_table_manager; - KMemoryBlockSlabManager Kernel::s_app_memory_block_manager; - KMemoryBlockSlabManager Kernel::s_sys_memory_block_manager; - KBlockInfoManager Kernel::s_block_info_manager; - KSupervisorPageTable Kernel::s_supervisor_page_table; - KSynchronization Kernel::s_synchronization; - KUnsafeMemory Kernel::s_unsafe_memory; - KWorkerTaskManager Kernel::s_worker_task_managers[KWorkerTaskManager::WorkerType_Count]; + constinit Kernel::State Kernel::s_state = Kernel::State::Invalid; + constinit KResourceLimit Kernel::s_system_resource_limit; + KMemoryManager Kernel::s_memory_manager; + constinit KPageTableManager Kernel::s_page_table_manager; + constinit KMemoryBlockSlabManager Kernel::s_app_memory_block_manager; + constinit KMemoryBlockSlabManager Kernel::s_sys_memory_block_manager; + constinit KBlockInfoManager Kernel::s_block_info_manager; + constinit KSupervisorPageTable Kernel::s_supervisor_page_table; + constinit KSynchronization Kernel::s_synchronization; + constinit KUnsafeMemory Kernel::s_unsafe_memory; + constinit KWorkerTaskManager Kernel::s_worker_task_managers[KWorkerTaskManager::WorkerType_Count]; namespace { - /* TODO: C++20 constinit */ std::array<KThread, cpu::NumCores> g_main_threads; - /* TODO: C++20 constinit */ std::array<KThread, cpu::NumCores> g_idle_threads; + constinit std::array<KThread, cpu::NumCores> g_main_threads; + constinit std::array<KThread, cpu::NumCores> g_idle_threads; } KThread &Kernel::GetMainThread(s32 core_id) { return g_main_threads[core_id]; } diff --git a/sept/sept-secondary/src/fs_utils.c b/sept/sept-secondary/src/fs_utils.c index 5b9b9f8fd..6426f67e5 100644 --- a/sept/sept-secondary/src/fs_utils.c +++ b/sept/sept-secondary/src/fs_utils.c @@ -23,13 +23,15 @@ FATFS sd_fs; static bool g_sd_mounted = false; static bool g_sd_initialized = false; static bool g_ahb_redirect_enabled = false; +sdmmc_t g_sd_sdmmc; +sdmmc_device_t g_sd_device; bool mount_sd(void) { /* Already mounted. */ if (g_sd_mounted) return true; - + /* Enable AHB redirection if necessary. */ if (!g_ahb_redirect_enabled) { mc_enable_ahb_redirect(); @@ -41,7 +43,7 @@ bool mount_sd(void) if (sdmmc_device_sd_init(&g_sd_device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_UHS_SDR104)) { g_sd_initialized = true; - + /* Mount SD. */ if (f_mount(&sd_fs, "", 1) == FR_OK) { print(SCREEN_LOG_LEVEL_INFO, "Mounted SD card!\n"); @@ -63,7 +65,7 @@ void unmount_sd(void) sdmmc_device_finish(&g_sd_device); g_sd_mounted = false; } - + /* Disable AHB redirection if necessary. */ if (g_ahb_redirect_enabled) { mc_disable_ahb_redirect(); @@ -81,13 +83,13 @@ uint32_t get_file_size(const char *filename) FIL f; if (f_open(&f, filename, FA_READ) != FR_OK) return 0; - + /* Get the file size. */ uint32_t file_size = f_size(&f); - + /* Close the file. */ f_close(&f); - + return file_size; } @@ -101,10 +103,10 @@ int read_from_file(void *dst, uint32_t dst_size, const char *filename) FIL f; if (f_open(&f, filename, FA_READ) != FR_OK) return 0; - + /* Sync. */ f_sync(&f); - + /* Read from file. */ UINT br = 0; int res = f_read(&f, dst, dst_size, &br); @@ -118,7 +120,7 @@ int write_to_file(void *src, uint32_t src_size, const char *filename) /* SD card hasn't been mounted yet. */ if (!g_sd_mounted) return 0; - + /* Open the file for writing. */ FIL f; if (f_open(&f, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) diff --git a/sept/sept-secondary/src/fs_utils.h b/sept/sept-secondary/src/fs_utils.h index 8cb0f326c..1e25b2b82 100644 --- a/sept/sept-secondary/src/fs_utils.h +++ b/sept/sept-secondary/src/fs_utils.h @@ -13,7 +13,7 @@ * 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 FUSEE_FS_UTILS_H #define FUSEE_FS_UTILS_H @@ -23,8 +23,8 @@ #include "sdmmc/sdmmc.h" #include "utils.h" -sdmmc_t g_sd_sdmmc; -sdmmc_device_t g_sd_device; +extern sdmmc_t g_sd_sdmmc; +extern sdmmc_device_t g_sd_device; bool mount_sd(void); void unmount_sd(void); diff --git a/sept/sept-secondary/src/lib/ini.c b/sept/sept-secondary/src/lib/ini.c index 63626c72d..ccf4640d2 100644 --- a/sept/sept-secondary/src/lib/ini.c +++ b/sept/sept-secondary/src/lib/ini.c @@ -70,7 +70,10 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(dest, src, size - 1); +#pragma GCC diagnostic pop dest[size - 1] = '\0'; return dest; } diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index 154166eb9..9de2611db 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "amsmitm_initialization.hpp" #include "amsmitm_module_management.hpp" #include "bpc_mitm/bpc_ams_power_utils.hpp" diff --git a/stratosphere/ams_mitm/source/amsmitm_module.hpp b/stratosphere/ams_mitm/source/amsmitm_module.hpp index 75333f47b..1c68f3200 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module.hpp +++ b/stratosphere/ams_mitm/source/amsmitm_module.hpp @@ -19,21 +19,25 @@ namespace ams::mitm { /* TODO: C++20 Concepts will make this a lot less stupid. */ - class ModuleBase {}; + template<typename T> + concept IsModule = requires(T, void *arg) { + { T::ThreadPriority } -> std::convertible_to<s32>; + { T::StackSize } -> std::convertible_to<size_t>; + { T::Stack } -> std::convertible_to<void *>; + { T::ThreadFunction(arg) } -> std::same_as<void>; + }; - #define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule : public ::ams::mitm::ModuleBase { \ - public: \ - static constexpr s32 ThreadPriority = prio; \ - static constexpr size_t StackSize = ss; \ - alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \ - public: \ - static void ThreadFunction(void *); \ + #define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule { \ + public: \ + static constexpr s32 ThreadPriority = prio; \ + static constexpr size_t StackSize = ss; \ + alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \ + public: \ + static void ThreadFunction(void *); \ } - template<class M> + template<class M> requires IsModule<M> struct ModuleTraits { - static_assert(std::is_base_of<ModuleBase, M>::value, "Mitm Modules must inherit from ams::mitm::Module"); - static constexpr void *Stack = &M::Stack[0]; static constexpr size_t StackSize = M::StackSize; diff --git a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp index e139c557a..ffa792fb9 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp @@ -54,7 +54,7 @@ namespace ams::mitm { .main = Traits::ThreadFunction, .stack_mem = Traits::Stack, .priority = Traits::ThreadPriority, - .stack_size = Traits::StackSize, + .stack_size = static_cast<u32>(Traits::StackSize), }; } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp index 9eb29bab5..e2733d18f 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "bpc_ams_module.hpp" #include "bpc_ams_service.hpp" diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index cc15f63f8..727cc61bc 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "bpc_ams_power_utils.hpp" #include "../amsmitm_fs_utils.hpp" diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp index 79d6c27e1..ad88aa1c1 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "bpc_ams_service.hpp" #include "bpc_ams_power_utils.hpp" diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp index 24e804c89..bf410a993 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "bpc_mitm_service.hpp" #include "bpc_ams_power_utils.hpp" diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp index f3d757bd4..36f3934a0 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "bpcmitm_module.hpp" #include "bpc_mitm_service.hpp" diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 380f12748..60aec454e 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_fs_utils.hpp" #include "../amsmitm_initialization.hpp" #include "fs_shim.h" diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp index 5b16af1d3..d30758196 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fsmitm_boot0storage.hpp" namespace ams::mitm::fs { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp index 3d27f0d84..a8a709287 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fsmitm_module.hpp" #include "fs_mitm_service.hpp" diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index cebd406bd..5adadd40a 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -40,7 +40,7 @@ namespace ams::mitm::fs { s64 file_table_size; s64 file_partition_ofs; }; - static_assert(std::is_pod<Header>::value && sizeof(Header) == 0x50); + static_assert(util::is_pod<Header>::value && sizeof(Header) == 0x50); struct DirectoryEntry { u32 parent; @@ -51,7 +51,7 @@ namespace ams::mitm::fs { u32 name_size; char name[]; }; - static_assert(std::is_pod<DirectoryEntry>::value && sizeof(DirectoryEntry) == 0x18); + static_assert(util::is_pod<DirectoryEntry>::value && sizeof(DirectoryEntry) == 0x18); struct FileEntry { u32 parent; @@ -62,7 +62,7 @@ namespace ams::mitm::fs { u32 name_size; char name[]; }; - static_assert(std::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20); + static_assert(util::is_pod<FileEntry>::value && sizeof(FileEntry) == 0x20); template<typename Entry> class TableReader { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_save_utils.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_save_utils.cpp index 65be46822..589fca44a 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_save_utils.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_save_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fsmitm_save_utils.hpp" namespace ams::mitm::fs { diff --git a/stratosphere/ams_mitm/source/hid_mitm/hid_mitm_service.cpp b/stratosphere/ams_mitm/source/hid_mitm/hid_mitm_service.cpp index 176fef43d..d516ab733 100644 --- a/stratosphere/ams_mitm/source/hid_mitm/hid_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/hid_mitm/hid_mitm_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "hid_mitm_service.hpp" #include "hid_shim.h" diff --git a/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.cpp b/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.cpp index 3423f0efc..c40d39b56 100644 --- a/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.cpp +++ b/stratosphere/ams_mitm/source/hid_mitm/hidmitm_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "hidmitm_module.hpp" #include "hid_mitm_service.hpp" diff --git a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.cpp b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.cpp index 30699c6e8..33db54be5 100644 --- a/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.cpp +++ b/stratosphere/ams_mitm/source/ns_mitm/nsmitm_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "nsmitm_module.hpp" #include "ns_am_mitm_service.hpp" diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp index 25becb56f..4c12ff57c 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "set_mitm_service.hpp" namespace ams::mitm::settings { diff --git a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp index 7b386ea2e..26637a092 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setmitm_module.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_initialization.hpp" #include "setmitm_module.hpp" #include "set_mitm_service.hpp" diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp index b10131814..130314ce0 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "setsys_mitm_service.hpp" #include "settings_sd_kvs.hpp" diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index d52e582d5..79f7374a9 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "../amsmitm_debug.hpp" #include "../amsmitm_fs_utils.hpp" #include "settings_sd_kvs.hpp" @@ -42,7 +43,7 @@ namespace ams::settings::fwdbg { } }; - static_assert(std::is_pod<SdKeyValueStoreEntry>::value); + static_assert(util::is_pod<SdKeyValueStoreEntry>::value); constexpr inline bool operator==(const SdKeyValueStoreEntry &lhs, const SdKeyValueStoreEntry &rhs) { if (lhs.HasValue() != rhs.HasValue()) { diff --git a/stratosphere/boot/Makefile b/stratosphere/boot/Makefile index 8fa028c65..d7e1a1e7a 100644 --- a/stratosphere/boot/Makefile +++ b/stratosphere/boot/Makefile @@ -121,6 +121,8 @@ $(OUTPUT).kip : $(OUTPUT).elf $(OUTPUT).elf : $(OFILES) +boot_power_utils.o: fusee_primary.bin.o fusee_primary_bin.h + #--------------------------------------------------------------------------------- # you need a rule like this for each extension you use as binary data #--------------------------------------------------------------------------------- diff --git a/stratosphere/boot/source/boot_battery_driver.cpp b/stratosphere/boot/source/boot_battery_driver.cpp index bd013cead..ae6a5b776 100644 --- a/stratosphere/boot/source/boot_battery_driver.cpp +++ b/stratosphere/boot/source/boot_battery_driver.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_battery_driver.hpp" #include "boot_calibration.hpp" #include "boot_i2c_utils.hpp" diff --git a/stratosphere/boot/source/boot_battery_icons.cpp b/stratosphere/boot/source/boot_battery_icons.cpp index 8809e17b0..af7b0e425 100644 --- a/stratosphere/boot/source/boot_battery_icons.cpp +++ b/stratosphere/boot/source/boot_battery_icons.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_battery_icons.hpp" #include "boot_display.hpp" diff --git a/stratosphere/boot/source/boot_boot_reason.cpp b/stratosphere/boot/source/boot_boot_reason.cpp index 2d2a05733..6cb6cb863 100644 --- a/stratosphere/boot/source/boot_boot_reason.cpp +++ b/stratosphere/boot/source/boot_boot_reason.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_boot_reason.hpp" #include "boot_pmic_driver.hpp" #include "boot_rtc_driver.hpp" diff --git a/stratosphere/boot/source/boot_calibration.cpp b/stratosphere/boot/source/boot_calibration.cpp index 8ae084e52..6257609f3 100644 --- a/stratosphere/boot/source/boot_calibration.cpp +++ b/stratosphere/boot/source/boot_calibration.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_calibration.hpp" namespace ams::boot { diff --git a/stratosphere/boot/source/boot_change_voltage.cpp b/stratosphere/boot/source/boot_change_voltage.cpp index 88853dfd7..b73f38b8f 100644 --- a/stratosphere/boot/source/boot_change_voltage.cpp +++ b/stratosphere/boot/source/boot_change_voltage.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_change_voltage.hpp" #include "boot_pmc_wrapper.hpp" diff --git a/stratosphere/boot/source/boot_charger_driver.cpp b/stratosphere/boot/source/boot_charger_driver.cpp index 69f4e193d..695d1be9a 100644 --- a/stratosphere/boot/source/boot_charger_driver.cpp +++ b/stratosphere/boot/source/boot_charger_driver.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_charger_driver.hpp" namespace ams::boot { diff --git a/stratosphere/boot/source/boot_check_battery.cpp b/stratosphere/boot/source/boot_check_battery.cpp index 3add32c54..63b6627e4 100644 --- a/stratosphere/boot/source/boot_check_battery.cpp +++ b/stratosphere/boot/source/boot_check_battery.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_battery_driver.hpp" #include "boot_battery_icons.hpp" #include "boot_boot_reason.hpp" diff --git a/stratosphere/boot/source/boot_check_clock.cpp b/stratosphere/boot/source/boot_check_clock.cpp index a3076e2ba..4ca02d079 100644 --- a/stratosphere/boot/source/boot_check_clock.cpp +++ b/stratosphere/boot/source/boot_check_clock.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_check_clock.hpp" #include "boot_power_utils.hpp" diff --git a/stratosphere/boot/source/boot_clock_initial_configuration.cpp b/stratosphere/boot/source/boot_clock_initial_configuration.cpp index b88dd0914..4dd90a83f 100644 --- a/stratosphere/boot/source/boot_clock_initial_configuration.cpp +++ b/stratosphere/boot/source/boot_clock_initial_configuration.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_clock_initial_configuration.hpp" #include "boot_pmc_wrapper.hpp" #include "boot_registers_pmc.hpp" diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 1ade06ae7..9b1a2b346 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_display.hpp" #include "boot_i2c_utils.hpp" #include "boot_pmc_wrapper.hpp" diff --git a/stratosphere/boot/source/boot_fan_enable.cpp b/stratosphere/boot/source/boot_fan_enable.cpp index adc3e78ea..26d0cf8a4 100644 --- a/stratosphere/boot/source/boot_fan_enable.cpp +++ b/stratosphere/boot/source/boot_fan_enable.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_fan_enable.hpp" #include "gpio/gpio_utils.hpp" diff --git a/stratosphere/boot/source/boot_i2c_utils.cpp b/stratosphere/boot/source/boot_i2c_utils.cpp index 8d5a8f9a5..541761b23 100644 --- a/stratosphere/boot/source/boot_i2c_utils.cpp +++ b/stratosphere/boot/source/boot_i2c_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_i2c_utils.hpp" namespace ams::boot { diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 5a3713262..a6688f24d 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_boot_reason.hpp" #include "boot_change_voltage.hpp" #include "boot_check_battery.hpp" diff --git a/stratosphere/boot/source/boot_pcv.cpp b/stratosphere/boot/source/boot_pcv.cpp index 73a56e757..59212a9aa 100644 --- a/stratosphere/boot/source/boot_pcv.cpp +++ b/stratosphere/boot/source/boot_pcv.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c/i2c_types.hpp" #include "i2c/driver/impl/i2c_pcv.hpp" #include "i2c/driver/impl/i2c_registers.hpp" diff --git a/stratosphere/boot/source/boot_pmc_wrapper.cpp b/stratosphere/boot/source/boot_pmc_wrapper.cpp index e88a0ae8f..9003009d4 100644 --- a/stratosphere/boot/source/boot_pmc_wrapper.cpp +++ b/stratosphere/boot/source/boot_pmc_wrapper.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_pmc_wrapper.hpp" namespace ams::boot { diff --git a/stratosphere/boot/source/boot_pmic_driver.cpp b/stratosphere/boot/source/boot_pmic_driver.cpp index 846bc09c1..8dbe39365 100644 --- a/stratosphere/boot/source/boot_pmic_driver.cpp +++ b/stratosphere/boot/source/boot_pmic_driver.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_i2c_utils.hpp" #include "boot_pmic_driver.hpp" diff --git a/stratosphere/boot/source/boot_power_utils.cpp b/stratosphere/boot/source/boot_power_utils.cpp index 9c140421b..3b18807b0 100644 --- a/stratosphere/boot/source/boot_power_utils.cpp +++ b/stratosphere/boot/source/boot_power_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_power_utils.hpp" #include "fusee-primary_bin.h" diff --git a/stratosphere/boot/source/boot_repair_boot_images.cpp b/stratosphere/boot/source/boot_repair_boot_images.cpp index 16095aa73..9c7047418 100644 --- a/stratosphere/boot/source/boot_repair_boot_images.cpp +++ b/stratosphere/boot/source/boot_repair_boot_images.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_power_utils.hpp" #include "boot_repair_boot_images.hpp" diff --git a/stratosphere/boot/source/boot_rtc_driver.cpp b/stratosphere/boot/source/boot_rtc_driver.cpp index e01e80d0b..33fb6fd98 100644 --- a/stratosphere/boot/source/boot_rtc_driver.cpp +++ b/stratosphere/boot/source/boot_rtc_driver.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_rtc_driver.hpp" namespace ams::boot { diff --git a/stratosphere/boot/source/boot_splash_screen.cpp b/stratosphere/boot/source/boot_splash_screen.cpp index ebffc543d..01b45aed5 100644 --- a/stratosphere/boot/source/boot_splash_screen.cpp +++ b/stratosphere/boot/source/boot_splash_screen.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_boot_reason.hpp" #include "boot_display.hpp" #include "boot_splash_screen.hpp" diff --git a/stratosphere/boot/source/boot_wake_pins.cpp b/stratosphere/boot/source/boot_wake_pins.cpp index f4921d051..ccfc94700 100644 --- a/stratosphere/boot/source/boot_wake_pins.cpp +++ b/stratosphere/boot/source/boot_wake_pins.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "boot_pmc_wrapper.hpp" #include "boot_wake_pins.hpp" diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp b/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp index 20d8eb7de..984c4423d 100644 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp +++ b/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp @@ -13,9 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include <stratosphere/spl.hpp> - +#include <stratosphere.hpp> #include "gpio_initial_configuration.hpp" #include "gpio_utils.hpp" diff --git a/stratosphere/boot/source/gpio/gpio_utils.cpp b/stratosphere/boot/source/gpio/gpio_utils.cpp index cba922c16..9f933e365 100644 --- a/stratosphere/boot/source/gpio/gpio_utils.cpp +++ b/stratosphere/boot/source/gpio/gpio_utils.cpp @@ -13,9 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include <stratosphere/reg.hpp> - +#include <stratosphere.hpp> #include "gpio_utils.hpp" namespace ams::gpio { diff --git a/stratosphere/boot/source/i2c/driver/i2c_api.cpp b/stratosphere/boot/source/i2c/driver/i2c_api.cpp index 06565597e..df9a1bda4 100644 --- a/stratosphere/boot/source/i2c/driver/i2c_api.cpp +++ b/stratosphere/boot/source/i2c/driver/i2c_api.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_api.hpp" #include "impl/i2c_resource_manager.hpp" diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp index f1bcf3dd7..fb419af32 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_pcv.hpp" #include "i2c_bus_accessor.hpp" diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp index e1affe153..d6b3f1a5f 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_driver_types.hpp" namespace ams::i2c::driver::impl { diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp index db93a3dfd..627ab3c26 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_pcv.hpp" #include "i2c_resource_manager.hpp" diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp index 5c2ad5ad6..8e982ec6e 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_session.hpp" namespace ams::i2c::driver::impl { diff --git a/stratosphere/boot/source/i2c/i2c_command_list.cpp b/stratosphere/boot/source/i2c/i2c_command_list.cpp index dc2cd6e8a..9b44658b4 100644 --- a/stratosphere/boot/source/i2c/i2c_command_list.cpp +++ b/stratosphere/boot/source/i2c/i2c_command_list.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "i2c_types.hpp" #include "i2c_command_list.hpp" diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp b/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp index d9a651b12..521dbe835 100644 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp +++ b/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pinmux_initial_configuration.hpp" #include "pinmux_utils.hpp" diff --git a/stratosphere/boot/source/pinmux/pinmux_utils.cpp b/stratosphere/boot/source/pinmux/pinmux_utils.cpp index 8c9b0433d..17ce295f8 100644 --- a/stratosphere/boot/source/pinmux/pinmux_utils.cpp +++ b/stratosphere/boot/source/pinmux/pinmux_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pinmux_utils.hpp" namespace ams::pinmux { diff --git a/stratosphere/boot2/source/boot2_main.cpp b/stratosphere/boot2/source/boot2_main.cpp index 2fa6c3b43..b7e7d4570 100644 --- a/stratosphere/boot2/source/boot2_main.cpp +++ b/stratosphere/boot2/source/boot2_main.cpp @@ -102,5 +102,7 @@ int main(int argc, char **argv) /* Launch all programs off of SYSTEM/the SD. */ boot2::LaunchPostSdCardBootPrograms(); + + return 0; } diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index c66a31aae..173d8c88f 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -13,8 +13,7 @@ * 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 <sys/stat.h> -#include <sys/types.h> +#include <stratosphere.hpp> #include "creport_crash_report.hpp" #include "creport_utils.hpp" @@ -351,7 +350,7 @@ namespace ams::creport { } void CrashReport::SaveToFile(ScopedFile &file) { - file.WriteFormat(u8"Atmosphère Crash Report (v1.5):\n"); + file.WriteFormat("Atmosphère Crash Report (v1.5):\n"); file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", this->result.GetValue(), this->result.GetModule(), this->result.GetDescription()); /* Process Info. */ diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index 3eb716049..fdd824efe 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "creport_crash_report.hpp" #include "creport_utils.hpp" diff --git a/stratosphere/creport/source/creport_modules.cpp b/stratosphere/creport/source/creport_modules.cpp index 5370aa453..49694ee9a 100644 --- a/stratosphere/creport/source/creport_modules.cpp +++ b/stratosphere/creport/source/creport_modules.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "creport_modules.hpp" #include "creport_utils.hpp" diff --git a/stratosphere/creport/source/creport_scoped_file.cpp b/stratosphere/creport/source/creport_scoped_file.cpp index d2c067801..916d7be56 100644 --- a/stratosphere/creport/source/creport_scoped_file.cpp +++ b/stratosphere/creport/source/creport_scoped_file.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "creport_scoped_file.hpp" namespace ams::creport { diff --git a/stratosphere/creport/source/creport_threads.cpp b/stratosphere/creport/source/creport_threads.cpp index 175c2d49b..0a3373ac7 100644 --- a/stratosphere/creport/source/creport_threads.cpp +++ b/stratosphere/creport/source/creport_threads.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "creport_threads.hpp" #include "creport_modules.hpp" diff --git a/stratosphere/creport/source/creport_utils.cpp b/stratosphere/creport/source/creport_utils.cpp index 6ec66eac3..ee54a4e68 100644 --- a/stratosphere/creport/source/creport_utils.cpp +++ b/stratosphere/creport/source/creport_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "creport_utils.hpp" namespace ams::creport { diff --git a/stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp b/stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp index 316fc3291..b3856f441 100644 --- a/stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp +++ b/stratosphere/dmnt/source/cheat/dmnt_cheat_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_cheat_service.hpp" #include "impl/dmnt_cheat_api.hpp" diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 302dcb22f..6369937ef 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_cheat_api.hpp" #include "dmnt_cheat_vm.hpp" #include "dmnt_cheat_debug_events_manager.hpp" @@ -141,7 +142,7 @@ namespace ams::dmnt::cheat::impl { } /* Clear metadata. */ - static_assert(std::is_pod<decltype(this->cheat_process_metadata)>::value, "CheatProcessMetadata definition!"); + static_assert(util::is_pod<decltype(this->cheat_process_metadata)>::value, "CheatProcessMetadata definition!"); std::memset(&this->cheat_process_metadata, 0, sizeof(this->cheat_process_metadata)); /* Clear cheat list. */ diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 54ad300c8..3cc7dd057 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_cheat_debug_events_manager.hpp" /* WORKAROUND: This design prevents a kernel deadlock from occurring on 6.0.0+ */ diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp index ea03e95c5..f3bb39036 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp @@ -13,7 +13,7 @@ * 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 <sys/stat.h> +#include <stratosphere.hpp> #include "dmnt_cheat_vm.hpp" #include "dmnt_cheat_api.hpp" diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index d321fbbde..12aded756 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_service.hpp" #include "cheat/dmnt_cheat_service.hpp" #include "cheat/impl/dmnt_cheat_api.hpp" diff --git a/stratosphere/dmnt/source/dmnt_service.hpp b/stratosphere/dmnt/source/dmnt_service.hpp index 27aa3f187..80ba6de55 100644 --- a/stratosphere/dmnt/source/dmnt_service.hpp +++ b/stratosphere/dmnt/source/dmnt_service.hpp @@ -55,7 +55,7 @@ namespace ams::dmnt { } }; - static_assert(std::is_pod<TargetIOFileHandle>::value && sizeof(TargetIOFileHandle) == sizeof(u64), "TargetIOFileHandle"); + static_assert(util::is_pod<TargetIOFileHandle>::value && sizeof(TargetIOFileHandle) == sizeof(u64), "TargetIOFileHandle"); class DebugMonitorService final : public sf::IServiceObject { private: diff --git a/stratosphere/dmnt/source/dmnt_service_debug.cpp b/stratosphere/dmnt/source/dmnt_service_debug.cpp index 8b32b1d07..06a5e7351 100644 --- a/stratosphere/dmnt/source/dmnt_service_debug.cpp +++ b/stratosphere/dmnt/source/dmnt_service_debug.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_service.hpp" namespace ams::dmnt { diff --git a/stratosphere/dmnt/source/dmnt_service_target_io.cpp b/stratosphere/dmnt/source/dmnt_service_target_io.cpp index 8274596dc..b4ecb1d62 100644 --- a/stratosphere/dmnt/source/dmnt_service_target_io.cpp +++ b/stratosphere/dmnt/source/dmnt_service_target_io.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "dmnt_service.hpp" namespace std { diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index bbebe52a6..38889fb2e 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_config.hpp" namespace ams::fatal::srv { @@ -77,21 +78,21 @@ namespace ams::fatal::srv { /* Setup messages. */ { - this->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n"; + this->error_msg = "Error Code: 2%03d-%04d (0x%x)\n"; - this->error_desc = u8"An error has occured.\n\n" - u8"Please press the POWER Button to restart the console normally, or a VOL button\n" - u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" - u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" - u8"If the problem persists, refer to the Nintendo Support Website.\n" - u8"support.nintendo.com/switch/error\n"; + this->error_desc = "An error has occured.\n\n" + "Please press the POWER Button to restart the console normally, or a VOL button\n" + "to reboot to a payload (or RCM, if none is present). If you are unable to\n" + "restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" + "If the problem persists, refer to the Nintendo Support Website.\n" + "support.nintendo.com/switch/error\n"; /* If you're running Atmosphere on a quest unit for some reason, talk to me on discord. */ - this->quest_desc = u8"Please call 1-800-875-1852 for service.\n\n" - u8"Also, please be aware that running Atmosphere on a Quest device is not fully\n" - u8"supported. Perhaps try booting your device without Atmosphere before calling\n" - u8"an official Nintendo service hotline. If you encounter further issues, please\n" - u8"contact SciresM#0524 on Discord, or via some other means.\n"; + this->quest_desc = "Please call 1-800-875-1852 for service.\n\n" + "Also, please be aware that running Atmosphere on a Quest device is not fully\n" + "supported. Perhaps try booting your device without Atmosphere before calling\n" + "an official Nintendo service hotline. If you encounter further issues, please\n" + "contact SciresM#0524 on Discord, or via some other means.\n"; /* TODO: Try to load dynamically? */ /* FsStorage message_storage; */ diff --git a/stratosphere/fatal/source/fatal_debug.cpp b/stratosphere/fatal/source/fatal_debug.cpp index 60007e35e..7f4188c55 100644 --- a/stratosphere/fatal/source/fatal_debug.cpp +++ b/stratosphere/fatal/source/fatal_debug.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_debug.hpp" #include "fatal_config.hpp" diff --git a/stratosphere/fatal/source/fatal_event_manager.cpp b/stratosphere/fatal/source/fatal_event_manager.cpp index b5e02602f..8a8cbb2f3 100644 --- a/stratosphere/fatal/source/fatal_event_manager.cpp +++ b/stratosphere/fatal/source/fatal_event_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_event_manager.hpp" namespace ams::fatal::srv { diff --git a/stratosphere/fatal/source/fatal_font.cpp b/stratosphere/fatal/source/fatal_font.cpp index 1a1cfe892..67752e9d4 100644 --- a/stratosphere/fatal/source/fatal_font.cpp +++ b/stratosphere/fatal/source/fatal_font.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_config.hpp" #include "fatal_font.hpp" diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index af942d211..f1708ba51 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_service.hpp" #include "fatal_config.hpp" #include "fatal_repair.hpp" diff --git a/stratosphere/fatal/source/fatal_repair.cpp b/stratosphere/fatal/source/fatal_repair.cpp index ffd353398..bc57fe698 100644 --- a/stratosphere/fatal/source/fatal_repair.cpp +++ b/stratosphere/fatal/source/fatal_repair.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_repair.hpp" #include "fatal_service_for_self.hpp" diff --git a/stratosphere/fatal/source/fatal_scoped_file.cpp b/stratosphere/fatal/source/fatal_scoped_file.cpp index 4b47a2813..4f3359255 100644 --- a/stratosphere/fatal/source/fatal_scoped_file.cpp +++ b/stratosphere/fatal/source/fatal_scoped_file.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_scoped_file.hpp" namespace ams::fatal::srv { diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp index d3a886fed..0e86883b6 100644 --- a/stratosphere/fatal/source/fatal_service.cpp +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_config.hpp" #include "fatal_debug.hpp" #include "fatal_service.hpp" diff --git a/stratosphere/fatal/source/fatal_task.cpp b/stratosphere/fatal/source/fatal_task.cpp index 3354118d5..00195b946 100644 --- a/stratosphere/fatal/source/fatal_task.cpp +++ b/stratosphere/fatal/source/fatal_task.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_task.hpp" #include "fatal_task_error_report.hpp" diff --git a/stratosphere/fatal/source/fatal_task_clock.cpp b/stratosphere/fatal/source/fatal_task_clock.cpp index 933ef6a22..1b253a184 100644 --- a/stratosphere/fatal/source/fatal_task_clock.cpp +++ b/stratosphere/fatal/source/fatal_task_clock.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_task_clock.hpp" namespace ams::fatal::srv { diff --git a/stratosphere/fatal/source/fatal_task_error_report.cpp b/stratosphere/fatal/source/fatal_task_error_report.cpp index 6744be8ec..8247ac02d 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.cpp +++ b/stratosphere/fatal/source/fatal_task_error_report.cpp @@ -13,8 +13,7 @@ * 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 <sys/stat.h> -#include <sys/types.h> +#include <stratosphere.hpp> #include "fatal_config.hpp" #include "fatal_task_error_report.hpp" #include "fatal_scoped_file.hpp" @@ -79,13 +78,13 @@ namespace ams::fatal::srv { std::snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/%011lu_%016lx.log", timestamp, static_cast<u64>(this->context->program_id)); ScopedFile file(file_path); if (file.IsOpen()) { - file.WriteFormat(u8"Atmosphère Fatal Report (v1.1):\n"); + file.WriteFormat("Atmosphère Fatal Report (v1.1):\n"); file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", this->context->result.GetValue(), this->context->result.GetModule(), this->context->result.GetDescription()); file.WriteFormat("Program ID: %016lx\n", static_cast<u64>(this->context->program_id)); if (strlen(this->context->proc_name)) { file.WriteFormat("Process Name: %s\n", this->context->proc_name); } - file.WriteFormat(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig().GetFirmwareVersion().display_version, ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); + file.WriteFormat("Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig().GetFirmwareVersion().display_version, ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); if (this->context->cpu_ctx.architecture == CpuContext::Architecture_Aarch32) { file.WriteFormat("General Purpose Registers:\n"); diff --git a/stratosphere/fatal/source/fatal_task_power.cpp b/stratosphere/fatal/source/fatal_task_power.cpp index b543a89df..69d84ef46 100644 --- a/stratosphere/fatal/source/fatal_task_power.cpp +++ b/stratosphere/fatal/source/fatal_task_power.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_config.hpp" #include "fatal_task_power.hpp" diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index dbd78690f..327904f9d 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_task_screen.hpp" #include "fatal_config.hpp" #include "fatal_font.hpp" @@ -213,18 +214,18 @@ namespace ams::fatal::srv { font::AddSpacingLines(0.5f); font::PrintFormatLine( "Program: %016lX", static_cast<u64>(this->context->program_id)); font::AddSpacingLines(0.5f); - font::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", config.GetFirmwareVersion().display_version, ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); + font::PrintFormatLine("Firmware: %s (Atmosphère %u.%u.%u-%s)", config.GetFirmwareVersion().display_version, ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); font::AddSpacingLines(1.5f); if (!exosphere::ResultVersionMismatch::Includes(this->context->result)) { font::Print(config.GetErrorDescription()); } else { /* Print a special message for atmosphere version mismatch. */ - font::Print(u8"Atmosphère version mismatch detected.\n\n" - u8"Please press the POWER Button to restart the console normally, or a VOL button\n" - u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" - u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" - u8"Please ensure that all Atmosphère components are updated.\n" - u8"github.com/Atmosphere-NX/Atmosphere/releases\n"); + font::Print("Atmosphère version mismatch detected.\n\n" + "Please press the POWER Button to restart the console normally, or a VOL button\n" + "to reboot to a payload (or RCM, if none is present). If you are unable to\n" + "restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" + "Please ensure that all Atmosphère components are updated.\n" + "github.com/Atmosphere-NX/Atmosphere/releases\n"); } /* Add a line. */ diff --git a/stratosphere/fatal/source/fatal_task_sound.cpp b/stratosphere/fatal/source/fatal_task_sound.cpp index 5210722a0..cc235b7d5 100644 --- a/stratosphere/fatal/source/fatal_task_sound.cpp +++ b/stratosphere/fatal/source/fatal_task_sound.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "fatal_task_sound.hpp" namespace ams::fatal::srv { diff --git a/stratosphere/loader/source/ldr_arguments.cpp b/stratosphere/loader/source/ldr_arguments.cpp index 6127454c3..0c4452cce 100644 --- a/stratosphere/loader/source/ldr_arguments.cpp +++ b/stratosphere/loader/source/ldr_arguments.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_arguments.hpp" namespace ams::ldr::args { diff --git a/stratosphere/loader/source/ldr_capabilities.cpp b/stratosphere/loader/source/ldr_capabilities.cpp index 7e3198097..6c170863c 100644 --- a/stratosphere/loader/source/ldr_capabilities.cpp +++ b/stratosphere/loader/source/ldr_capabilities.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_capabilities.hpp" namespace ams::ldr::caps { diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index 3fa6a766f..9a622d639 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_content_management.hpp" namespace ams::ldr { diff --git a/stratosphere/loader/source/ldr_development_manager.cpp b/stratosphere/loader/source/ldr_development_manager.cpp index bb48a635b..cf15a3e3f 100644 --- a/stratosphere/loader/source/ldr_development_manager.cpp +++ b/stratosphere/loader/source/ldr_development_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_launch_record.hpp" namespace ams::ldr { diff --git a/stratosphere/loader/source/ldr_launch_record.cpp b/stratosphere/loader/source/ldr_launch_record.cpp index fc7e48e9c..ec97f813c 100644 --- a/stratosphere/loader/source/ldr_launch_record.cpp +++ b/stratosphere/loader/source/ldr_launch_record.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_launch_record.hpp" namespace ams::ldr { diff --git a/stratosphere/loader/source/ldr_loader_service.cpp b/stratosphere/loader/source/ldr_loader_service.cpp index 65954ac78..9b3a0e1c0 100644 --- a/stratosphere/loader/source/ldr_loader_service.cpp +++ b/stratosphere/loader/source/ldr_loader_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_arguments.hpp" #include "ldr_content_management.hpp" #include "ldr_development_manager.hpp" diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index 5d1c16ebf..c22308e6f 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - +#include <stratosphere.hpp> #include "ldr_development_manager.hpp" #include "ldr_loader_service.hpp" diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp index 0da9a3338..bb8255b32 100644 --- a/stratosphere/loader/source/ldr_meta.cpp +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" #include "ldr_development_manager.hpp" diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index 2209b111b..a9b14da9d 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_patcher.hpp" namespace ams::ldr { diff --git a/stratosphere/loader/source/ldr_ro_manager.cpp b/stratosphere/loader/source/ldr_ro_manager.cpp index 901d22137..d96cd6bdc 100644 --- a/stratosphere/loader/source/ldr_ro_manager.cpp +++ b/stratosphere/loader/source/ldr_ro_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ldr_ro_manager.hpp" namespace ams::ldr::ro { diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index 7b6465655..8bb8760e2 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <stratosphere.hpp> extern "C" { diff --git a/stratosphere/pgl/source/pgl_main.cpp b/stratosphere/pgl/source/pgl_main.cpp index 03a75f246..bd56bebc1 100644 --- a/stratosphere/pgl/source/pgl_main.cpp +++ b/stratosphere/pgl/source/pgl_main.cpp @@ -64,7 +64,7 @@ namespace ams::pgl { constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl"); constexpr size_t ShellMaxSessions = 8; /* Official maximum is 8. */ - /* TODO: C++20 constinit */ pgl::srv::ShellInterface g_shell_interface; + constinit pgl::srv::ShellInterface g_shell_interface; ALWAYS_INLINE std::shared_ptr<pgl::srv::ShellInterface> GetSharedPointerToShellInterface() { return ams::sf::ServiceObjectTraits<pgl::srv::ShellInterface>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_shell_interface)); diff --git a/stratosphere/pm/source/impl/pm_process_info.cpp b/stratosphere/pm/source/impl/pm_process_info.cpp index 7965a9384..a8252eab5 100644 --- a/stratosphere/pm/source/impl/pm_process_info.cpp +++ b/stratosphere/pm/source/impl/pm_process_info.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_process_info.hpp" namespace ams::pm::impl { diff --git a/stratosphere/pm/source/impl/pm_process_manager.cpp b/stratosphere/pm/source/impl/pm_process_manager.cpp index 3d761ca9d..7a761817a 100644 --- a/stratosphere/pm/source/impl/pm_process_manager.cpp +++ b/stratosphere/pm/source/impl/pm_process_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_process_manager.hpp" #include "pm_resource_manager.hpp" diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index aeedc11ab..a9ba8277e 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_resource_manager.hpp" namespace ams::pm::resource { diff --git a/stratosphere/pm/source/pm_boot_mode_service.cpp b/stratosphere/pm/source/pm_boot_mode_service.cpp index 8c2df555b..e185ddf0a 100644 --- a/stratosphere/pm/source/pm_boot_mode_service.cpp +++ b/stratosphere/pm/source/pm_boot_mode_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_boot_mode_service.hpp" namespace ams::pm::bm { diff --git a/stratosphere/pm/source/pm_debug_monitor_service.cpp b/stratosphere/pm/source/pm_debug_monitor_service.cpp index 4430fee73..a42156a55 100644 --- a/stratosphere/pm/source/pm_debug_monitor_service.cpp +++ b/stratosphere/pm/source/pm_debug_monitor_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_debug_monitor_service.hpp" #include "impl/pm_process_manager.hpp" diff --git a/stratosphere/pm/source/pm_info_service.cpp b/stratosphere/pm/source/pm_info_service.cpp index 5626c3ff4..d6e34e30b 100644 --- a/stratosphere/pm/source/pm_info_service.cpp +++ b/stratosphere/pm/source/pm_info_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_info_service.hpp" #include "impl/pm_process_manager.hpp" diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index f25586032..3efc9cabc 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_boot_mode_service.hpp" #include "pm_debug_monitor_service.hpp" #include "pm_info_service.hpp" diff --git a/stratosphere/pm/source/pm_shell_service.cpp b/stratosphere/pm/source/pm_shell_service.cpp index 9b471707a..939066dea 100644 --- a/stratosphere/pm/source/pm_shell_service.cpp +++ b/stratosphere/pm/source/pm_shell_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "pm_shell_service.hpp" #include "impl/pm_process_manager.hpp" diff --git a/stratosphere/ro/source/impl/ro_nro_utils.cpp b/stratosphere/ro/source/impl/ro_nro_utils.cpp index 90de057e9..aee7d153f 100644 --- a/stratosphere/ro/source/impl/ro_nro_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nro_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_nro_utils.hpp" namespace ams::ro::impl { diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp index 46b124531..f4cbfc7a1 100644 --- a/stratosphere/ro/source/impl/ro_nrr_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_nrr_utils.hpp" #include "ro_service_impl.hpp" diff --git a/stratosphere/ro/source/impl/ro_patcher.cpp b/stratosphere/ro/source/impl/ro_patcher.cpp index 992d27f4a..0b9242521 100644 --- a/stratosphere/ro/source/impl/ro_patcher.cpp +++ b/stratosphere/ro/source/impl/ro_patcher.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_patcher.hpp" namespace ams::ro::impl { diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 784900667..3fde492fa 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_nrr_utils.hpp" #include "ro_nro_utils.hpp" #include "ro_patcher.hpp" diff --git a/stratosphere/ro/source/ro_debug_monitor.cpp b/stratosphere/ro/source/ro_debug_monitor.cpp index 78bcd26a3..2cb4aa8ee 100644 --- a/stratosphere/ro/source/ro_debug_monitor.cpp +++ b/stratosphere/ro/source/ro_debug_monitor.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_debug_monitor.hpp" #include "impl/ro_service_impl.hpp" diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index 0f10add7f..aa0a1e912 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_debug_monitor.hpp" #include "ro_service.hpp" diff --git a/stratosphere/ro/source/ro_service.cpp b/stratosphere/ro/source/ro_service.cpp index 05654587e..96d38519f 100644 --- a/stratosphere/ro/source/ro_service.cpp +++ b/stratosphere/ro/source/ro_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "ro_service.hpp" #include "impl/ro_service_impl.hpp" diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index 82302a8e3..33812ddb3 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_service_manager.hpp" namespace ams::sm::impl { diff --git a/stratosphere/sm/source/sm_dmnt_service.cpp b/stratosphere/sm/source/sm_dmnt_service.cpp index 584bc5928..7ee32f363 100644 --- a/stratosphere/sm/source/sm_dmnt_service.cpp +++ b/stratosphere/sm/source/sm_dmnt_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_dmnt_service.hpp" #include "impl/sm_service_manager.hpp" diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index c4e9720fd..eb2c56dfc 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_user_service.hpp" #include "sm_manager_service.hpp" #include "sm_dmnt_service.hpp" diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 230bf9e8b..0bc806933 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_manager_service.hpp" #include "impl/sm_service_manager.hpp" diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 56ed8b878..f36c661e7 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "sm_user_service.hpp" #include "impl/sm_service_manager.hpp" diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index d21497229..49592e8b1 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_ctr_drbg.hpp" diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp index f358764b3..4bd5bbe06 100644 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_crypto_service.hpp" diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp index e8ebbbca5..0be399815 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.cpp +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_ctr_drbg.hpp" namespace ams::spl { diff --git a/stratosphere/spl/source/spl_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp index ead676cd4..545c6a2b5 100644 --- a/stratosphere/spl/source/spl_deprecated_service.cpp +++ b/stratosphere/spl/source/spl_deprecated_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_deprecated_service.hpp" diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp index 723e1db9c..4c9e08952 100644 --- a/stratosphere/spl/source/spl_es_service.cpp +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_es_service.hpp" diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index 8e201b9ae..8d3c9ce8b 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_fs_service.hpp" diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp index 58c60b922..cb87c37fb 100644 --- a/stratosphere/spl/source/spl_general_service.cpp +++ b/stratosphere/spl/source/spl_general_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_general_service.hpp" diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index fe0df90b2..6ffc5a290 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_random_service.hpp" diff --git a/stratosphere/spl/source/spl_manu_service.cpp b/stratosphere/spl/source/spl_manu_service.cpp index 0c1c80066..17cc8bcce 100644 --- a/stratosphere/spl/source/spl_manu_service.cpp +++ b/stratosphere/spl/source/spl_manu_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_manu_service.hpp" diff --git a/stratosphere/spl/source/spl_random_service.cpp b/stratosphere/spl/source/spl_random_service.cpp index 3b1e52ac7..6eae44a8f 100644 --- a/stratosphere/spl/source/spl_random_service.cpp +++ b/stratosphere/spl/source/spl_random_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_random_service.hpp" diff --git a/stratosphere/spl/source/spl_rsa_service.cpp b/stratosphere/spl/source/spl_rsa_service.cpp index 1bb41f13c..c14d01834 100644 --- a/stratosphere/spl/source/spl_rsa_service.cpp +++ b/stratosphere/spl/source/spl_rsa_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_rsa_service.hpp" diff --git a/stratosphere/spl/source/spl_ssl_service.cpp b/stratosphere/spl/source/spl_ssl_service.cpp index 860f00715..b6c8eeb44 100644 --- a/stratosphere/spl/source/spl_ssl_service.cpp +++ b/stratosphere/spl/source/spl_ssl_service.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stratosphere.hpp> #include "spl_api_impl.hpp" #include "spl_ssl_service.hpp" diff --git a/thermosphere/src/lib/ini.c b/thermosphere/src/lib/ini.c index 63626c72d..ccf4640d2 100644 --- a/thermosphere/src/lib/ini.c +++ b/thermosphere/src/lib/ini.c @@ -70,7 +70,10 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" strncpy(dest, src, size - 1); +#pragma GCC diagnostic pop dest[size - 1] = '\0'; return dest; } From 81f91803ecde50a5007729ff36c7ee2b2357b4bf Mon Sep 17 00:00:00 2001 From: SciresM <Sciresm@gmail.com> Date: Mon, 11 May 2020 15:04:51 -0700 Subject: [PATCH 026/118] Implement support for parsing/interacting with NCAs. (#942) * fs: implement support for interacting with ncas. * spl: extend to use virtual keyslots --- .../include/stratosphere/fs.hpp | 2 + .../stratosphere/fs/fs_file_storage.hpp | 33 +- .../stratosphere/fs/fs_memory_management.hpp | 11 + .../stratosphere/fs/fs_query_range.hpp | 2 + .../stratosphere/fs/fs_save_data_types.hpp | 10 + .../stratosphere/fs/fs_speed_emulation.hpp | 32 + .../stratosphere/fs/fs_storage_type.hpp | 27 + .../include/stratosphere/fssrv.hpp | 12 +- .../fssrv_partition_file_system_creator.hpp | 31 + .../fssrv_rom_file_system_creator.hpp | 33 + .../fssrv_storage_on_nca_creator.hpp | 52 + .../fssrv/fssrv_file_system_proxy_api.hpp | 35 + .../fssrv/fssrv_i_file_system_creator.hpp | 78 ++ .../fssrv_memory_resource_from_exp_heap.hpp | 74 + ...emory_resource_from_standard_allocator.hpp | 51 + .../fssrv/fssrv_nca_crypto_configuration.hpp | 23 + .../include/stratosphere/fssystem.hpp | 21 +- .../buffers/fssystem_buffer_manager_utils.hpp | 36 + .../fssystem_file_system_buffer_manager.hpp | 295 ++++ .../buffers/fssystem_i_buffer_manager.hpp | 110 ++ .../fssystem/dbm/fssystem_dbm_utils.hpp | 43 + .../fssystem/fssystem_acid_sign_key.hpp | 111 -- ...ystem_aes_ctr_counter_extended_storage.hpp | 116 ++ .../fssystem_alignment_matching_storage.hpp | 309 +++++ ...system_alignment_matching_storage_impl.hpp | 28 + .../fssystem/fssystem_allocator_utility.hpp | 64 + .../fssystem/fssystem_bucket_tree.hpp | 330 +++++ .../fssystem_bucket_tree_template_impl.hpp | 165 +++ .../fssystem/fssystem_bucket_tree_utils.hpp | 85 ++ .../fssystem_crypto_configuration.hpp | 34 + .../fssystem_file_system_proxy_api.hpp | 33 + .../fssystem/fssystem_indirect_storage.hpp | 163 +++ ...ssystem_indirect_storage_template_impl.hpp | 113 ++ .../fssystem_integrity_romfs_storage.hpp | 74 + .../fssystem_nca_file_system_driver.hpp | 226 +++ .../fssystem_nca_file_system_driver_impl.hpp | 150 ++ .../fssystem/fssystem_nca_header.hpp | 275 ++++ .../fssystem/fssystem_sparse_storage.hpp | 97 ++ ...fssystem_speed_emulation_configuration.hpp | 28 + .../fssystem_block_cache_buffered_storage.hpp | 170 +++ .../save/fssystem_buffered_storage.hpp | 80 ++ ...rchical_integrity_verification_storage.hpp | 205 +++ .../fssystem/save/fssystem_i_save_file.hpp | 23 + .../fssystem_i_save_file_system_driver.hpp | 23 + ...ssystem_integrity_verification_storage.hpp | 95 ++ .../fssystem/save/fssystem_save_types.hpp | 41 + .../stratosphere/hos/hos_stratosphere_api.hpp | 1 + .../include/stratosphere/spl/spl_api.hpp | 82 +- .../include/stratosphere/spl/spl_types.hpp | 45 +- .../source/fs/fs_file_storage.cpp | 12 + .../fssrv_partition_file_system_creator.cpp | 33 + .../fssrv_rom_file_system_creator.cpp | 70 + .../fssrv_storage_on_nca_creator.cpp | 166 +++ .../fssrv/fssrv_file_system_proxy_api.cpp | 24 + .../fssrv_memory_resource_from_exp_heap.cpp | 57 + ...emory_resource_from_standard_allocator.cpp | 53 + .../fssrv/fssrv_nca_crypto_configuration.cpp | 188 +++ .../fssystem_file_system_buffer_manager.cpp | 356 +++++ ...ystem_aes_ctr_counter_extended_storage.cpp | 296 ++++ ...system_alignment_matching_storage_impl.cpp | 256 ++++ .../fssystem/fssystem_allocator_utility.cpp | 65 + .../source/fssystem/fssystem_bucket_tree.cpp | 544 ++++++++ .../fssystem_crypto_configuration.cpp | 232 ++++ .../fssystem_file_system_proxy_api.cpp | 152 ++ .../fssystem_hierarchical_sha256_storage.cpp | 184 +++ .../fssystem_hierarchical_sha256_storage.hpp | 57 + .../fssystem/fssystem_indirect_storage.cpp | 177 +++ .../fssystem_integrity_romfs_storage.cpp | 45 + .../fssystem/fssystem_key_slot_cache.hpp | 133 ++ .../fssystem/fssystem_lru_list_cache.hpp | 89 ++ .../fssystem_nca_file_system_driver.cpp | 1053 ++++++++++++++ .../source/fssystem/fssystem_nca_header.cpp | 31 + .../source/fssystem/fssystem_nca_reader.cpp | 434 ++++++ ...fssystem_read_only_block_cache_storage.hpp | 139 ++ .../fssystem/fssystem_sparse_storage.cpp | 44 + ...fssystem_speed_emulation_configuration.cpp | 34 + .../fssystem_block_cache_buffered_storage.cpp | 1234 +++++++++++++++++ .../save/fssystem_buffered_storage.cpp | 1082 +++++++++++++++ ...rchical_integrity_verification_storage.cpp | 352 +++++ ...ssystem_integrity_verification_storage.cpp | 484 +++++++ .../source/hos/hos_stratosphere_api.cpp | 8 + .../source/hos/hos_version_api.cpp | 8 + .../source/hos/hos_version_api_private.hpp | 1 + .../source/ncm/ncm_content_storage_impl.cpp | 2 +- .../libstratosphere/source/spl/spl_api.cpp | 270 +++- .../libvapours/include/vapours/crypto.hpp | 2 + .../vapours/crypto/crypto_hmac_generator.hpp | 51 + .../crypto/crypto_hmac_sha1_generator.hpp | 27 + .../crypto/crypto_hmac_sha256_generator.hpp | 27 + .../vapours/crypto/impl/crypto_hmac_impl.hpp | 126 ++ .../include/vapours/results/fs_results.hpp | 207 ++- libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_bitutil.hpp | 7 + .../include/vapours/util/util_variadic.hpp | 84 ++ .../crypto/crypto_hmac_sha1_generator.cpp | 28 + .../crypto/crypto_hmac_sha256_generator.cpp | 28 + .../source/amsmitm_initialization.cpp | 10 +- stratosphere/ams_mitm/source/amsmitm_main.cpp | 4 +- stratosphere/boot/source/boot_boot_reason.cpp | 25 +- stratosphere/boot/source/boot_display.cpp | 17 +- stratosphere/boot/source/boot_main.cpp | 4 +- stratosphere/loader/source/ldr_main.cpp | 10 +- stratosphere/loader/source/ldr_meta.cpp | 12 +- .../loader/source/ldr_process_creation.cpp | 4 +- stratosphere/ncm/source/ncm_main.cpp | 4 +- stratosphere/pm/source/pm_main.cpp | 4 +- stratosphere/ro/source/ro_main.cpp | 7 +- stratosphere/spl/source/spl_api_impl.cpp | 255 +++- stratosphere/spl/source/spl_api_impl.hpp | 14 +- .../spl/source/spl_crypto_service.cpp | 10 +- .../spl/source/spl_crypto_service.hpp | 16 +- .../spl/source/spl_deprecated_service.cpp | 14 +- .../spl/source/spl_deprecated_service.hpp | 20 +- stratosphere/spl/source/spl_es_service.cpp | 2 +- stratosphere/spl/source/spl_es_service.hpp | 2 +- stratosphere/spl/source/spl_fs_service.cpp | 2 +- stratosphere/spl/source/spl_fs_service.hpp | 2 +- .../spl/source/spl_key_slot_cache.hpp | 138 ++ 118 files changed, 13301 insertions(+), 405 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_speed_emulation.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fs/fs_storage_type.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_partition_file_system_creator.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_rom_file_system_creator.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fssrv_file_system_proxy_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_exp_heap.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_standard_allocator.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssrv/fssrv_nca_crypto_configuration.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/dbm/fssystem_dbm_utils.hpp delete mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_acid_sign_key.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_allocator_utility.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_utils.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_crypto_configuration.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_file_system_proxy_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage_template_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_sparse_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_speed_emulation_configuration.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_buffered_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_save_types.hpp create mode 100644 libraries/libstratosphere/source/fssrv/fscreator/fssrv_partition_file_system_creator.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fscreator/fssrv_rom_file_system_creator.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fssrv_file_system_proxy_api.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_exp_heap.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_standard_allocator.cpp create mode 100644 libraries/libstratosphere/source/fssrv/fssrv_nca_crypto_configuration.cpp create mode 100644 libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_alignment_matching_storage_impl.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_allocator_utility.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_crypto_configuration.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_indirect_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_key_slot_cache.hpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_lru_list_cache.hpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_sparse_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_speed_emulation_configuration.cpp create mode 100644 libraries/libstratosphere/source/fssystem/save/fssystem_block_cache_buffered_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp create mode 100644 libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_hmac_generator.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_hmac_sha1_generator.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_hmac_sha256_generator.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/impl/crypto_hmac_impl.hpp create mode 100644 libraries/libvapours/include/vapours/util/util_variadic.hpp create mode 100644 libraries/libvapours/source/crypto/crypto_hmac_sha1_generator.cpp create mode 100644 libraries/libvapours/source/crypto/crypto_hmac_sha256_generator.cpp create mode 100644 stratosphere/spl/source/spl_key_slot_cache.hpp diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index d7f89ad64..96093da58 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -16,6 +16,7 @@ #pragma once #include <stratosphere/fs/fs_common.hpp> +#include <stratosphere/fs/fs_storage_type.hpp> #include <stratosphere/fs/fsa/fs_ifile.hpp> #include <stratosphere/fs/fsa/fs_idirectory.hpp> #include <stratosphere/fs/fsa/fs_ifilesystem.hpp> @@ -29,6 +30,7 @@ #include <stratosphere/fs/fs_remote_storage.hpp> #include <stratosphere/fs/fs_file_storage.hpp> #include <stratosphere/fs/fs_query_range.hpp> +#include <stratosphere/fs/fs_speed_emulation.hpp> #include <stratosphere/fs/impl/fs_common_mount_name.hpp> #include <stratosphere/fs/fs_mount.hpp> #include <stratosphere/fs/fs_path_tool.hpp> diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp index a6b3855a6..3d8f44ec7 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_file_storage.hpp @@ -17,11 +17,14 @@ #include <stratosphere/fs/fs_common.hpp> #include <stratosphere/fs/fs_istorage.hpp> #include <stratosphere/fs/fsa/fs_ifile.hpp> +#include <stratosphere/fs/fsa/fs_ifilesystem.hpp> #include <stratosphere/fs/impl/fs_newable.hpp> namespace ams::fs { class FileStorage : public IStorage, public impl::Newable { + NON_COPYABLE(FileStorage); + NON_MOVEABLE(FileStorage); private: static constexpr s64 InvalidSize = -1; private: @@ -43,8 +46,25 @@ namespace ams::fs { } virtual ~FileStorage() { /* ... */ } - protected: + private: Result UpdateSize(); + protected: + constexpr FileStorage() : unique_file(), shared_file(), base_file(nullptr), size(InvalidSize) { /* ... */ } + + void SetFile(fs::fsa::IFile *file) { + AMS_ASSERT(file != nullptr); + AMS_ASSERT(this->base_file == nullptr); + this->base_file = file; + } + + void SetFile(std::unique_ptr<fs::fsa::IFile> &&file) { + AMS_ASSERT(file != nullptr); + AMS_ASSERT(this->base_file == nullptr); + AMS_ASSERT(this->unique_file == nullptr); + + this->unique_file = std::move(file); + this->base_file = this->unique_file.get(); + } public: virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; @@ -54,6 +74,17 @@ namespace ams::fs { virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; }; + class FileStorageBasedFileSystem : public FileStorage { + NON_COPYABLE(FileStorageBasedFileSystem); + NON_MOVEABLE(FileStorageBasedFileSystem); + private: + std::shared_ptr<fs::fsa::IFileSystem> base_file_system; + public: + constexpr FileStorageBasedFileSystem() : FileStorage(), base_file_system(nullptr) { /* ... */ } + + Result Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const char *path, fs::OpenMode mode); + }; + class FileHandleStorage : public IStorage, public impl::Newable { private: static constexpr s64 InvalidSize = -1; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp index 03e344745..abe362650 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp @@ -46,6 +46,17 @@ namespace ams::fs { return std::unique_ptr<T, Deleter>(static_cast<T *>(::ams::fs::impl::Allocate(sizeof(T))), Deleter(sizeof(T))); } + template<typename ArrayT> + std::unique_ptr<ArrayT, Deleter> MakeUnique(size_t size) { + using T = typename std::remove_extent<ArrayT>::type; + + static_assert(std::is_pod<ArrayT>::value); + static_assert(std::is_array<ArrayT>::value); + + const size_t alloc_size = sizeof(T) * size; + return std::unique_ptr<ArrayT, Deleter>(static_cast<T *>(::ams::fs::impl::Allocate(alloc_size)), Deleter(alloc_size)); + } + } } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp index 1c0fbdebf..2456f48db 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_query_range.hpp @@ -47,6 +47,8 @@ namespace ams::fs { enum class AesCtrKeyTypeFlag : s32 { InternalKeyForSoftwareAes = (1 << 0), + InternalKeyForHardwareAes = (1 << 1), + ExternalKeyForHardwareAes = (1 << 2), }; } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp index e663d67dc..83387f837 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -156,4 +156,14 @@ namespace ams::fs { static_assert(sizeof(SaveDataExtraData) == 0x200); static_assert(util::is_pod<SaveDataExtraData>::value); + struct HashSalt { + static constexpr size_t Size = 32; + + u8 value[Size]; + }; + static_assert(std::is_pod<HashSalt>::value); + static_assert(sizeof(HashSalt) == HashSalt::Size); + + using SaveDataHashSalt = std::optional<HashSalt>; + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_speed_emulation.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_speed_emulation.hpp new file mode 100644 index 000000000..73155a8f4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_speed_emulation.hpp @@ -0,0 +1,32 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere/fs/fs_common.hpp> + +namespace ams::fs { + + enum class SpeedEmulationMode { + None = 0, + Faster = 1, + Slower = 2, + Random = 3, + }; + + /* TODO */ + /* Result SetSpeedEmulationMode(SpeedEmulationMode mode); */ + /* Result GetSpeedEmulationMode(SpeedEmulationMode *out); */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_storage_type.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_storage_type.hpp new file mode 100644 index 000000000..a5ebbb643 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_storage_type.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fs { + + enum StorageType : s32 { + StorageType_SaveData = 0, + StorageType_RomFs = 1, + StorageType_Authoring = 2, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv.hpp b/libraries/libstratosphere/include/stratosphere/fssrv.hpp index f0f74dde0..91d839512 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv.hpp @@ -15,5 +15,13 @@ */ #pragma once -#include "fssrv/fssrv_sf_path.hpp" -#include "fssrv/fssrv_path_normalizer.hpp" +#include <stratosphere/fssrv/fssrv_sf_path.hpp> +#include <stratosphere/fssrv/fssrv_path_normalizer.hpp> +#include <stratosphere/fssrv/fssrv_nca_crypto_configuration.hpp> +#include <stratosphere/fssrv/fssrv_memory_resource_from_standard_allocator.hpp> +#include <stratosphere/fssrv/fssrv_memory_resource_from_exp_heap.hpp> +#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp> +#include <stratosphere/fssrv/fscreator/fssrv_partition_file_system_creator.hpp> +#include <stratosphere/fssrv/fscreator/fssrv_rom_file_system_creator.hpp> +#include <stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp> +#include <stratosphere/fssrv/fssrv_file_system_proxy_api.hpp> diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_partition_file_system_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_partition_file_system_creator.hpp new file mode 100644 index 000000000..8c1ab29d8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_partition_file_system_creator.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp> + +namespace ams::fssrv::fscreator { + + class PartitionFileSystemCreator : public IPartitionFileSystemCreator { + NON_COPYABLE(PartitionFileSystemCreator); + NON_MOVEABLE(PartitionFileSystemCreator); + public: + PartitionFileSystemCreator() { /* ... */ } + + virtual Result Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_rom_file_system_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_rom_file_system_creator.hpp new file mode 100644 index 000000000..339983c35 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_rom_file_system_creator.hpp @@ -0,0 +1,33 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp> + +namespace ams::fssrv::fscreator { + + class RomFileSystemCreator : public IRomFileSystemCreator { + NON_COPYABLE(RomFileSystemCreator); + NON_MOVEABLE(RomFileSystemCreator); + private: + MemoryResource *allocator; + public: + explicit RomFileSystemCreator(MemoryResource *mr) : allocator(mr) { /* ... */ } + + virtual Result Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp new file mode 100644 index 000000000..a95edfc43 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp @@ -0,0 +1,52 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp> +#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> + +namespace ams::fssystem { + + struct NcaCryptoConfiguration; + +} + +namespace ams::fssrv::fscreator { + + class StorageOnNcaCreator : public IStorageOnNcaCreator { + NON_COPYABLE(StorageOnNcaCreator); + NON_MOVEABLE(StorageOnNcaCreator); + private: + MemoryResource *allocator; + fssystem::IBufferManager * const buffer_manager; + const fssystem::NcaCryptoConfiguration &nca_crypto_cfg; + bool is_prod; + bool is_enabled_program_verification; + private: + Result VerifyNcaHeaderSign2(fssystem::NcaReader *nca_reader, fs::IStorage *storage); + public: + explicit StorageOnNcaCreator(MemoryResource *mr, const fssystem::NcaCryptoConfiguration &cfg, bool prod, fssystem::IBufferManager *bm) : allocator(mr), buffer_manager(bm), nca_crypto_cfg(cfg), is_prod(prod), is_enabled_program_verification(true) { + /* ... */ + } + + virtual Result Create(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) override; + virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) override; + virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) override; + virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) override; + virtual void SetEnabledProgramVerification(bool en) override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_file_system_proxy_api.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_file_system_proxy_api.hpp new file mode 100644 index 000000000..cc7fdf410 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_file_system_proxy_api.hpp @@ -0,0 +1,35 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssrv::fscreator { + + struct FileSystemCreatorInterfaces; + +} + +namespace ams::fssystem { + + class IBufferManager; + +} + +namespace ams::fssrv { + + void InitializeForFileSystemProxy(fscreator::FileSystemCreatorInterfaces *fs_creator_interfaces, fssystem::IBufferManager *buffer_manager, bool is_development_function_enabled); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp new file mode 100644 index 000000000..9b6235980 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp @@ -0,0 +1,78 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/impl/fs_newable.hpp> + +namespace ams::fs { + + class IStorage; + enum class BisPartitionId; + + namespace fsa { + + class IFileSystem; + + } + +} + +namespace ams::fssystem { + + class NcaReader; + class NcaFsHeaderReader; + + namespace save { + + /* TODO */ + + } + +} + +namespace ams::fssrv::fscreator { + + class IRomFileSystemCreator { + public: + virtual ~IRomFileSystemCreator() { /* ... */ } + virtual Result Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) = 0; + }; + + class IPartitionFileSystemCreator { + public: + virtual ~IPartitionFileSystemCreator() { /* ... */ } + virtual Result Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) = 0; + }; + + class IStorageOnNcaCreator { + public: + virtual ~IStorageOnNcaCreator() { /* ... */ } + virtual Result Create(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) = 0; + virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) = 0; + virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) = 0; + virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) = 0; + virtual void SetEnabledProgramVerification(bool en) = 0; + }; + + struct FileSystemCreatorInterfaces { + IRomFileSystemCreator *rom_fs_creator; + IPartitionFileSystemCreator *partition_fs_creator; + IStorageOnNcaCreator *storage_on_nca_creator; + /* TODO: More creators. */ + }; + static_assert(std::is_pod<FileSystemCreatorInterfaces>::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_exp_heap.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_exp_heap.hpp new file mode 100644 index 000000000..08540f29e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_exp_heap.hpp @@ -0,0 +1,74 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> +#include <stratosphere/lmem/lmem_exp_heap.hpp> + +namespace ams::fssrv { + + class MemoryResourceFromExpHeap : public ams::MemoryResource { + private: + lmem::HeapHandle heap_handle; + public: + constexpr explicit MemoryResourceFromExpHeap(lmem::HeapHandle handle) : heap_handle(handle) { /* ... */ } + protected: + virtual void *AllocateImpl(size_t size, size_t align) override { + return lmem::AllocateFromExpHeap(this->heap_handle, size, static_cast<s32>(align)); + } + + virtual void DeallocateImpl(void *p, size_t size, size_t align) override { + return lmem::FreeToExpHeap(this->heap_handle, p); + } + + virtual bool IsEqualImpl(const MemoryResource &rhs) const override { + return false; + } + }; + + class PeakCheckableMemoryResourceFromExpHeap : public ams::MemoryResource { + private: + lmem::HeapHandle heap_handle; + os::Mutex mutex; + size_t peak_free_size; + size_t current_free_size; + public: + constexpr explicit PeakCheckableMemoryResourceFromExpHeap(size_t heap_size) : heap_handle(nullptr), mutex(false), peak_free_size(heap_size), current_free_size(heap_size) { /* ... */ } + + void SetHeapHandle(lmem::HeapHandle handle) { + this->heap_handle = handle; + } + + size_t GetPeakFreeSize() const { return this->peak_free_size; } + size_t GetCurrentFreeSize() const { return this->current_free_size; } + + void ClearPeak() { this->peak_free_size = this->current_free_size; } + + std::scoped_lock<os::Mutex> GetScopedLock() { + return std::scoped_lock(this->mutex); + } + + void OnAllocate(void *p, size_t size); + void OnDeallocate(void *p, size_t size); + protected: + virtual void *AllocateImpl(size_t size, size_t align) override; + virtual void DeallocateImpl(void *p, size_t size, size_t align) override; + virtual bool IsEqualImpl(const MemoryResource &rhs) const override { + return false; + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_standard_allocator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_standard_allocator.hpp new file mode 100644 index 000000000..7c8119883 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_memory_resource_from_standard_allocator.hpp @@ -0,0 +1,51 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> + +namespace ams::mem { + + class StandardAllocator; + +} + +namespace ams::fssrv { + + class MemoryResourceFromStandardAllocator : public ams::MemoryResource { + private: + mem::StandardAllocator *allocator; + os::SdkMutex mutex; + size_t peak_free_size; + size_t current_free_size; + size_t peak_allocated_size; + public: + explicit MemoryResourceFromStandardAllocator(mem::StandardAllocator *allocator); + public: + size_t GetPeakFreeSize() const { return this->peak_free_size; } + size_t GetCurrentFreeSize() const { return this->current_free_size; } + size_t GetPeakAllocatedSize() const { return this->peak_allocated_size; } + + void ClearPeak(); + protected: + virtual void *AllocateImpl(size_t size, size_t align) override; + virtual void DeallocateImpl(void *p, size_t size, size_t align) override; + virtual bool IsEqualImpl(const MemoryResource &rhs) const override { + return false; + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_nca_crypto_configuration.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_nca_crypto_configuration.hpp new file mode 100644 index 000000000..3199ddc5d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_nca_crypto_configuration.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp> + +namespace ams::fssrv { + + const ::ams::fssystem::NcaCryptoConfiguration *GetDefaultNcaCryptoConfiguration(bool prod); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp index 10385e0e6..2be11f318 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem.hpp @@ -15,9 +15,10 @@ */ #pragma once +#include <stratosphere/fssystem/fssystem_allocator_utility.hpp> #include <stratosphere/fssystem/fssystem_utility.hpp> +#include <stratosphere/fssystem/fssystem_speed_emulation_configuration.hpp> #include <stratosphere/fssystem/fssystem_external_code.hpp> -#include <stratosphere/fssystem/fssystem_acid_sign_key.hpp> #include <stratosphere/fssystem/fssystem_partition_file_system.hpp> #include <stratosphere/fssystem/fssystem_partition_file_system_meta.hpp> #include <stratosphere/fssystem/fssystem_path_tool.hpp> @@ -28,6 +29,22 @@ #include <stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp> #include <stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp> #include <stratosphere/fssystem/fssystem_romfs_file_system.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp> +#include <stratosphere/fssystem/fssystem_indirect_storage.hpp> +#include <stratosphere/fssystem/fssystem_indirect_storage_template_impl.hpp> +#include <stratosphere/fssystem/fssystem_sparse_storage.hpp> +#include <stratosphere/fssystem/fssystem_nca_header.hpp> +#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp> +#include <stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp> +#include <stratosphere/fssystem/fssystem_crypto_configuration.hpp> +#include <stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp> #include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp> -#include <stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp> +#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp> #include <stratosphere/fssystem/fssystem_pooled_buffer.hpp> +#include <stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp> +#include <stratosphere/fssystem/fssystem_alignment_matching_storage.hpp> +#include <stratosphere/fssystem/save/fssystem_buffered_storage.hpp> +#include <stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp> +#include <stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp> +#include <stratosphere/fssystem/fssystem_file_system_proxy_api.hpp> \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp index a2a69ac6c..db1e01bce 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp @@ -87,4 +87,40 @@ namespace ams::fssystem::buffers { } }; + template<typename IsValidBufferFunction> + Result AllocateBufferUsingBufferManagerContext(std::pair<uintptr_t, size_t> *out, fssystem::IBufferManager *buffer_manager, size_t size, const IBufferManager::BufferAttribute attribute, IsValidBufferFunction is_valid_buffer, const char *func_name) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(buffer_manager != nullptr); + AMS_ASSERT(func_name != nullptr); + + /* Clear the output. */ + *out = std::pair<uintptr_t, size_t>(0, 0); + + /* Get the context. */ + auto context = GetBufferManagerContext(); + + auto AllocateBufferImpl = [=]() -> Result { + auto buffer = buffer_manager->AllocateBuffer(size, attribute); + if (!is_valid_buffer(buffer)) { + if (buffer.first != 0) { + buffer_manager->DeallocateBuffer(buffer.first, buffer.second); + } + return fs::ResultBufferAllocationFailed(); + } + *out = buffer; + return ResultSuccess(); + }; + + if (context == nullptr || !context->IsNeedBlocking()) { + /* If there's no context (or we don't need to block), just allocate the buffer. */ + R_TRY(AllocateBufferImpl()); + } else { + /* Otherwise, try to allocate repeatedly. */ + R_TRY(DoContinuouslyUntilBufferIsAllocated(AllocateBufferImpl, func_name)); + } + + AMS_ASSERT(out->first != 0); + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp new file mode 100644 index 000000000..507fe6803 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp @@ -0,0 +1,295 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/lmem.hpp> +#include <stratosphere/fs/fs_memory_management.hpp> +#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> +#include <stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp> + +namespace ams::fssystem { + + class FileSystemBufferManager : public IBufferManager { + NON_COPYABLE(FileSystemBufferManager); + NON_MOVEABLE(FileSystemBufferManager); + public: + using BuddyHeap = FileSystemBuddyHeap; + private: + class CacheHandleTable { + NON_COPYABLE(CacheHandleTable); + NON_MOVEABLE(CacheHandleTable); + private: + class Entry { + private: + CacheHandle handle; + uintptr_t address; + size_t size; + BufferAttribute attr; + public: + constexpr void Initialize(CacheHandle h, uintptr_t a, size_t sz, BufferAttribute t) { + this->handle = h; + this->address = a; + this->size = sz; + this->attr = t; + } + + constexpr CacheHandle GetHandle() const { + return this->handle; + } + + constexpr uintptr_t GetAddress() const { + return this->address; + } + + constexpr size_t GetSize() const { + return this->size; + } + + constexpr BufferAttribute GetBufferAttribute() const { + return this->attr; + } + }; + + class AttrInfo : public util::IntrusiveListBaseNode<AttrInfo>, public ::ams::fs::impl::Newable { + NON_COPYABLE(AttrInfo); + NON_MOVEABLE(AttrInfo); + private: + s32 level; + s32 cache_count; + size_t cache_size; + public: + constexpr AttrInfo(s32 l, s32 cc, size_t cs) : level(l), cache_count(cc), cache_size(cs) { + /* ... */ + } + + constexpr s32 GetLevel() const { + return this->level; + } + + constexpr s32 GetCacheCount() const { + return this->cache_count; + } + + constexpr void IncrementCacheCount() { + ++this->cache_count; + } + + constexpr void DecrementCacheCount() { + --this->cache_count; + } + + constexpr size_t GetCacheSize() const { + return this->cache_size; + } + + constexpr void AddCacheSize(size_t diff) { + this->cache_size += diff; + } + + constexpr void SubtractCacheSize(size_t diff) { + AMS_ASSERT(this->cache_size >= diff); + this->cache_size -= diff; + } + + using Newable::operator new; + using Newable::operator delete; + static void *operator new(size_t, void *p) { + return p; + } + + static void operator delete(void *, size_t, void*) { /* ... */ } + }; + + using AttrListTraits = util::IntrusiveListBaseTraits<AttrInfo>; + using AttrList = typename AttrListTraits::ListType; + private: + std::unique_ptr<char[], ::ams::fs::impl::Deleter> internal_entry_buffer; + char *external_entry_buffer; + size_t entry_buffer_size; + Entry *entries; + s32 entry_count; + s32 entry_count_max; + AttrList attr_list; + char *external_attr_info_buffer; + s32 external_attr_info_count; + s32 cache_count_min; + size_t cache_size_min; + size_t total_cache_size; + CacheHandle current_handle; + public: + static constexpr size_t QueryWorkBufferSize(s32 max_cache_count) { + AMS_ASSERT(max_cache_count > 0); + const auto entry_size = sizeof(Entry) * max_cache_count; + const auto attr_list_size = sizeof(AttrInfo) * 0x100; + return util::AlignUp(entry_size + attr_list_size + alignof(Entry) + alignof(AttrInfo), 8); + } + public: + CacheHandleTable() : internal_entry_buffer(), external_entry_buffer(), entry_buffer_size(), entries(), entry_count(), entry_count_max(), attr_list(), external_attr_info_buffer(), external_attr_info_count(), cache_count_min(), cache_size_min(), total_cache_size(), current_handle() { + /* ... */ + } + + ~CacheHandleTable() { + this->Finalize(); + } + + Result Initialize(s32 max_cache_count); + Result Initialize(s32 max_cache_count, void *work, size_t work_size) { + const auto aligned_entry_buf = util::AlignUp(reinterpret_cast<uintptr_t>(work), alignof(Entry)); + this->external_entry_buffer = reinterpret_cast<char *>(aligned_entry_buf); + this->entry_buffer_size = sizeof(Entry) * max_cache_count; + + const auto aligned_attr_info_buf = util::AlignUp(reinterpret_cast<uintptr_t>(this->external_entry_buffer + this->entry_buffer_size), alignof(AttrInfo)); + const auto work_end = reinterpret_cast<uintptr_t>(work) + work_size; + this->external_attr_info_buffer = reinterpret_cast<char *>(aligned_attr_info_buf); + this->external_attr_info_count = static_cast<s32>((work_end - aligned_attr_info_buf) / sizeof(AttrInfo)); + + return ResultSuccess(); + } + + void Finalize(); + + bool Register(CacheHandle *out, uintptr_t address, size_t size, const BufferAttribute &attr); + bool Unregister(uintptr_t *out_address, size_t *out_size, CacheHandle handle); + + + bool UnregisterOldest(uintptr_t *out_address, size_t *out_size, const BufferAttribute &attr, size_t required_size = 0); + + CacheHandle PublishCacheHandle(); + + size_t GetTotalCacheSize() const; + private: + void UnregisterCore(uintptr_t *out_address, size_t *out_size, Entry *entry); + + Entry *AcquireEntry(uintptr_t address, size_t size, const BufferAttribute &attr); + + void ReleaseEntry(Entry *entry); + + AttrInfo *FindAttrInfo(const BufferAttribute &attr); + + s32 GetCacheCountMin(const BufferAttribute &attr) { + return this->cache_count_min; + } + + size_t GetCacheSizeMin(const BufferAttribute &attr) { + return this->cache_size_min; + } + }; + private: + BuddyHeap buddy_heap; + CacheHandleTable cache_handle_table; + size_t total_size; + size_t peak_free_size; + size_t peak_total_allocatable_size; + size_t retried_count; + mutable os::Mutex mutex; + public: + static constexpr size_t QueryWorkBufferSize(s32 max_cache_count, s32 max_order) { + const auto buddy_size = FileSystemBuddyHeap::QueryWorkBufferSize(max_order); + const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count); + return buddy_size + table_size; + } + public: + FileSystemBufferManager() : total_size(), peak_free_size(), peak_total_allocatable_size(), retried_count(), mutex(true) { /* ... */ } + + virtual ~FileSystemBufferManager() { /* ... */ } + + Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size) { + AMS_ASSERT(buffer_size > 0); + R_TRY(this->cache_handle_table.Initialize(max_cache_count)); + R_TRY(this->buddy_heap.Initialize(address, buffer_size, block_size)); + + this->total_size = this->buddy_heap.GetTotalFreeSize(); + this->peak_free_size = this->total_size; + this->peak_total_allocatable_size = this->total_size; + + return ResultSuccess(); + } + + Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, s32 max_order) { + AMS_ASSERT(buffer_size > 0); + R_TRY(this->cache_handle_table.Initialize(max_cache_count)); + R_TRY(this->buddy_heap.Initialize(address, buffer_size, block_size, max_order)); + + this->total_size = this->buddy_heap.GetTotalFreeSize(); + this->peak_free_size = this->total_size; + this->peak_total_allocatable_size = this->total_size; + + return ResultSuccess(); + } + + Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, void *work, size_t work_size) { + const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count); + const auto buddy_size = work_size - table_size; + AMS_ASSERT(work_size > table_size); + const auto table_buffer = static_cast<char *>(work); + const auto buddy_buffer = table_buffer + table_size; + + R_TRY(this->cache_handle_table.Initialize(max_cache_count, table_buffer, table_size)); + R_TRY(this->buddy_heap.Initialize(address, buffer_size, block_size, buddy_buffer, buddy_size)); + + this->total_size = this->buddy_heap.GetTotalFreeSize(); + this->peak_free_size = this->total_size; + this->peak_total_allocatable_size = this->total_size; + + return ResultSuccess(); + } + + Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, s32 max_order, void *work, size_t work_size) { + const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count); + const auto buddy_size = work_size - table_size; + AMS_ASSERT(work_size > table_size); + const auto table_buffer = static_cast<char *>(work); + const auto buddy_buffer = table_buffer + table_size; + + R_TRY(this->cache_handle_table.Initialize(max_cache_count, table_buffer, table_size)); + R_TRY(this->buddy_heap.Initialize(address, buffer_size, block_size, max_order, buddy_buffer, buddy_size)); + + this->total_size = this->buddy_heap.GetTotalFreeSize(); + this->peak_free_size = this->total_size; + this->peak_total_allocatable_size = this->total_size; + + return ResultSuccess(); + } + + void Finalize() { + this->buddy_heap.Finalize(); + this->cache_handle_table.Finalize(); + } + private: + virtual const std::pair<uintptr_t, size_t> AllocateBufferImpl(size_t size, const BufferAttribute &attr) override; + + virtual void DeallocateBufferImpl(uintptr_t address, size_t size) override; + + virtual CacheHandle RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) override; + + virtual const std::pair<uintptr_t, size_t> AcquireCacheImpl(CacheHandle handle) override; + + virtual size_t GetTotalSizeImpl() const override; + + virtual size_t GetFreeSizeImpl() const override; + + virtual size_t GetTotalAllocatableSizeImpl() const override; + + virtual size_t GetPeakFreeSizeImpl() const override; + + virtual size_t GetPeakTotalAllocatableSizeImpl() const override; + + virtual size_t GetRetriedCountImpl() const override; + + virtual void ClearPeakImpl() override; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp new file mode 100644 index 000000000..b5d8140fd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp @@ -0,0 +1,110 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem { + + class IBufferManager { + public: + class BufferAttribute { + private: + s32 level; + public: + constexpr BufferAttribute() : level(0) { /* ... */ } + constexpr explicit BufferAttribute(s32 l) : level(l) { /* ... */ } + + constexpr s32 GetLevel() const { return this->level; } + }; + + using CacheHandle = s64; + + static constexpr s32 BufferLevelMin = 0; + public: + virtual ~IBufferManager() { /* ... */ } + + const std::pair<uintptr_t, size_t> AllocateBuffer(size_t size, const BufferAttribute &attr) { + return this->AllocateBufferImpl(size, attr); + } + + const std::pair<uintptr_t, size_t> AllocateBuffer(size_t size) { + return this->AllocateBufferImpl(size, BufferAttribute()); + } + + void DeallocateBuffer(uintptr_t address, size_t size) { + return this->DeallocateBufferImpl(address, size); + } + + CacheHandle RegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) { + return this->RegisterCacheImpl(address, size, attr); + } + + const std::pair<uintptr_t, size_t> AcquireCache(CacheHandle handle) { + return this->AcquireCacheImpl(handle); + } + + size_t GetTotalSize() const { + return this->GetTotalSizeImpl(); + } + + size_t GetFreeSize() const { + return this->GetFreeSizeImpl(); + } + + size_t GetTotalAllocatableSize() const { + return this->GetTotalAllocatableSizeImpl(); + } + + size_t GetPeakFreeSize() const { + return this->GetPeakFreeSizeImpl(); + } + + size_t GetPeakTotalAllocatableSize() const { + return this->GetPeakTotalAllocatableSizeImpl(); + } + + size_t GetRetriedCount() const { + return this->GetRetriedCountImpl(); + } + + void ClearPeak() const { + return this->ClearPeak(); + } + protected: + virtual const std::pair<uintptr_t, size_t> AllocateBufferImpl(size_t size, const BufferAttribute &attr) = 0; + + virtual void DeallocateBufferImpl(uintptr_t address, size_t size) = 0; + + virtual CacheHandle RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) = 0; + + virtual const std::pair<uintptr_t, size_t> AcquireCacheImpl(CacheHandle handle) = 0; + + virtual size_t GetTotalSizeImpl() const = 0; + + virtual size_t GetFreeSizeImpl() const = 0; + + virtual size_t GetTotalAllocatableSizeImpl() const = 0; + + virtual size_t GetPeakFreeSizeImpl() const = 0; + + virtual size_t GetPeakTotalAllocatableSizeImpl() const = 0; + + virtual size_t GetRetriedCountImpl() const = 0; + + virtual void ClearPeakImpl() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/dbm/fssystem_dbm_utils.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/dbm/fssystem_dbm_utils.hpp new file mode 100644 index 000000000..984509aff --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/dbm/fssystem_dbm_utils.hpp @@ -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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem::dbm { + + namespace { + + constexpr inline s32 CountLeadingZeros(u32 val) { + return util::CountLeadingZeros(val); + } + + constexpr inline s32 CountLeadingOnes(u32 val) { + return CountLeadingZeros(~val); + } + + inline u32 ReadU32(const u8 *buf, size_t index) { + u32 val; + std::memcpy(std::addressof(val), buf + index, sizeof(u32)); + return val; + } + + inline void WriteU32(u8 *buf, size_t index, u32 val) { + std::memcpy(buf + index, std::addressof(val), sizeof(u32)); + } + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_acid_sign_key.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_acid_sign_key.hpp deleted file mode 100644 index 716d50611..000000000 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_acid_sign_key.hpp +++ /dev/null @@ -1,111 +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/>. - */ -#pragma once -#include <vapours.hpp> - -namespace ams::fssystem { - - constexpr inline size_t AcidSignatureKeyGenerationMax = 1; - - constexpr inline size_t AcidSignatureKeyModulusSize = 0x100; - - constexpr inline const u8 AcidSignatureKeyModulusDev[AcidSignatureKeyGenerationMax + 1][AcidSignatureKeyModulusSize] = { - { - 0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89, - 0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87, - 0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C, - 0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B, - 0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5, - 0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32, - 0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53, - 0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4, - 0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA, - 0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B, - 0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F, - 0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33, - 0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C, - 0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3, - 0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0, - 0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69 - }, - { - 0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0, - 0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E, - 0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70, - 0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A, - 0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76, - 0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD, - 0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F, - 0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE, - 0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D, - 0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7, - 0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03, - 0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62, - 0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A, - 0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2, - 0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36, - 0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B - } - }; - - constexpr inline const u8 AcidSignatureKeyModulusProd[AcidSignatureKeyGenerationMax + 1][AcidSignatureKeyModulusSize] = { - { - 0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, - 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, - 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, - 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, - 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, - 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, - 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, - 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, - 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, - 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, - 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, - 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, - 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, - 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, - 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, - 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD - }, - { - 0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90, - 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, - 0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, - 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E, - 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, - 0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, - 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01, - 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, - 0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, - 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6, - 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, - 0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, - 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D, - 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, - 0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, - 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03 - } - }; - - static_assert(sizeof(AcidSignatureKeyModulusProd) == sizeof(AcidSignatureKeyModulusDev)); - - constexpr inline const u8 AcidSignatureKeyExponent[] = { - 0x01, 0x00, 0x01 - }; - - constexpr inline size_t AcidSignatureKeyExponentSize = util::size(AcidSignatureKeyExponent); - -} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp new file mode 100644 index 000000000..d1e198c85 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp @@ -0,0 +1,116 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssystem/fssystem_aes_ctr_storage.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree.hpp> + +namespace ams::fssystem { + + class AesCtrCounterExtendedStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(AesCtrCounterExtendedStorage); + NON_MOVEABLE(AesCtrCounterExtendedStorage); + public: + static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize; + static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize; + static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize; + static constexpr size_t NodeSize = 16_KB; + + using IAllocator = BucketTree::IAllocator; + + using DecryptFunction = void(*)(void *dst, size_t dst_size, s32 index, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size); + + class IDecryptor { + public: + virtual ~IDecryptor() { /* ... */ } + virtual void Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) = 0; + virtual bool HasExternalDecryptionKey() const = 0; + }; + + struct Entry { + u8 offset[sizeof(s64)]; + s32 reserved; + s32 generation; + + void SetOffset(s64 value) { + std::memcpy(this->offset, std::addressof(value), sizeof(s64)); + } + + s64 GetOffset() const { + s64 value; + std::memcpy(std::addressof(value), this->offset, sizeof(s64)); + return value; + } + }; + static_assert(sizeof(Entry) == 0x10); + static_assert(alignof(Entry) == 4); + static_assert(std::is_pod<Entry>::value); + public: + static constexpr s64 QueryHeaderStorageSize() { + return BucketTree::QueryHeaderStorageSize(); + } + + static constexpr s64 QueryNodeStorageSize(s32 entry_count) { + return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count); + } + + static constexpr s64 QueryEntryStorageSize(s32 entry_count) { + return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count); + } + + static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index); + static Result CreateSoftwareDecryptor(std::unique_ptr<IDecryptor> *out); + private: + BucketTree table; + fs::SubStorage data_storage; + u8 key[KeySize]; + u32 secure_value; + s64 counter_offset; + std::unique_ptr<IDecryptor> decryptor; + public: + AesCtrCounterExtendedStorage() : table(), data_storage(), secure_value(), counter_offset(), decryptor() { /* ... */ } + virtual ~AesCtrCounterExtendedStorage() { this->Finalize(); } + + Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, s64 counter_offset, fs::SubStorage data_storage, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count, std::unique_ptr<IDecryptor> &&decryptor); + void Finalize(); + + bool IsInitialized() const { return this->table.IsInitialized(); } + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + *out = this->table.GetSize(); + return ResultSuccess(); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageA(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageB(); + } + private: + Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp new file mode 100644 index 000000000..2ff67cea3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp @@ -0,0 +1,309 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/impl/fs_newable.hpp> +#include <stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp> +#include <stratosphere/fssystem/fssystem_pooled_buffer.hpp> + +namespace ams::fssystem { + + template<size_t _DataAlign, size_t _BufferAlign> + class AlignmentMatchingStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(AlignmentMatchingStorage); + NON_MOVEABLE(AlignmentMatchingStorage); + public: + static constexpr size_t DataAlign = _DataAlign; + static constexpr size_t BufferAlign = _BufferAlign; + + static constexpr size_t DataAlignMax = 0x200; + static_assert(DataAlign <= DataAlignMax); + static_assert(util::IsPowerOfTwo(DataAlign)); + static_assert(util::IsPowerOfTwo(BufferAlign)); + private: + std::shared_ptr<fs::IStorage> shared_base_storage; + fs::IStorage * const base_storage; + s64 base_storage_size; + bool is_base_storage_size_dirty; + public: + explicit AlignmentMatchingStorage(fs::IStorage *bs) : base_storage(bs), is_base_storage_size_dirty(true) { + /* ... */ + } + + explicit AlignmentMatchingStorage(std::shared_ptr<fs::IStorage> bs) : shared_base_storage(bs), base_storage(shared_base_storage.get()), is_base_storage_size_dirty(true) { + /* ... */ + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Allocate a work buffer on stack. */ + __attribute__((aligned(DataAlignMax))) char work_buf[DataAlign]; + static_assert(util::IsAligned(alignof(work_buf), BufferAlign)); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + return AlignmentMatchingStorageImpl::Read(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<char *>(buffer), size); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Allocate a work buffer on stack. */ + __attribute__((aligned(DataAlignMax))) char work_buf[DataAlign]; + static_assert(util::IsAligned(alignof(work_buf), BufferAlign)); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + return AlignmentMatchingStorageImpl::Write(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<const char *>(buffer), size); + } + + virtual Result Flush() override { + return this->base_storage->Flush(); + } + + virtual Result SetSize(s64 size) override { + ON_SCOPE_EXIT { this->is_base_storage_size_dirty = true; }; + return this->base_storage->SetSize(util::AlignUp(size, DataAlign)); + } + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + + if (this->is_base_storage_size_dirty) { + s64 size; + R_TRY(this->base_storage->GetSize(std::addressof(size))); + + this->base_storage_size = size; + this->is_base_storage_size_dirty = false; + } + + *out = this->base_storage_size; + return ResultSuccess(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); + + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, DataAlign); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, DataAlign); + const auto aligned_size = aligned_offset_end - aligned_offset; + + return this->base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } + }; + + template<size_t _BufferAlign> + class AlignmentMatchingStoragePooledBuffer : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(AlignmentMatchingStoragePooledBuffer); + NON_MOVEABLE(AlignmentMatchingStoragePooledBuffer); + public: + static constexpr size_t BufferAlign = _BufferAlign; + + static_assert(util::IsPowerOfTwo(BufferAlign)); + private: + fs::IStorage * const base_storage; + s64 base_storage_size; + size_t data_align; + bool is_base_storage_size_dirty; + public: + explicit AlignmentMatchingStoragePooledBuffer(fs::IStorage *bs, size_t da) : base_storage(bs), data_align(da), is_base_storage_size_dirty(true) { + AMS_ASSERT(util::IsPowerOfTwo(da)); + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + /* Allocate a pooled buffer. */ + PooledBuffer pooled_buffer; + pooled_buffer.AllocateParticularlyLarge(this->data_align, this->data_align); + + return AlignmentMatchingStorageImpl::Read(this->base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), this->data_align, BufferAlign, offset, static_cast<char *>(buffer), size); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + /* Allocate a pooled buffer. */ + PooledBuffer pooled_buffer; + pooled_buffer.AllocateParticularlyLarge(this->data_align, this->data_align); + + return AlignmentMatchingStorageImpl::Write(this->base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), this->data_align, BufferAlign, offset, static_cast<const char *>(buffer), size); + } + + virtual Result Flush() override { + return this->base_storage->Flush(); + } + + virtual Result SetSize(s64 size) override { + ON_SCOPE_EXIT { this->is_base_storage_size_dirty = true; }; + return this->base_storage->SetSize(util::AlignUp(size, this->data_align)); + } + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + + if (this->is_base_storage_size_dirty) { + s64 size; + R_TRY(this->base_storage->GetSize(std::addressof(size))); + + this->base_storage_size = size; + this->is_base_storage_size_dirty = false; + } + + *out = this->base_storage_size; + return ResultSuccess(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); + + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, this->data_align); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, this->data_align); + const auto aligned_size = aligned_offset_end - aligned_offset; + + return this->base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } + }; + + template<size_t _BufferAlign> + class AlignmentMatchingStorageInBulkRead : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(AlignmentMatchingStorageInBulkRead); + NON_MOVEABLE(AlignmentMatchingStorageInBulkRead); + public: + static constexpr size_t BufferAlign = _BufferAlign; + + static_assert(util::IsPowerOfTwo(BufferAlign)); + private: + std::shared_ptr<fs::IStorage> shared_base_storage; + fs::IStorage * const base_storage; + s64 base_storage_size; + size_t data_align; + public: + explicit AlignmentMatchingStorageInBulkRead(fs::IStorage *bs, size_t da) : shared_base_storage(), base_storage(bs), base_storage_size(-1), data_align(da) { + AMS_ASSERT(util::IsPowerOfTwo(this->data_align)); + } + + explicit AlignmentMatchingStorageInBulkRead(std::shared_ptr<fs::IStorage> bs, size_t da) : shared_base_storage(bs), base_storage(shared_base_storage.get()), base_storage_size(-1), data_align(da) { + AMS_ASSERT(util::IsPowerOfTwo(da)); + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + /* Allocate a pooled buffer. */ + PooledBuffer pooled_buffer(this->data_align, this->data_align); + return AlignmentMatchingStorageImpl::Write(this->base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), this->data_align, BufferAlign, offset, static_cast<const char *>(buffer), size); + } + + virtual Result Flush() override { + return this->base_storage->Flush(); + } + + virtual Result SetSize(s64 size) override { + ON_SCOPE_EXIT { this->base_storage_size = -1; }; + return this->base_storage->SetSize(util::AlignUp(size, this->data_align)); + } + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + + if (this->base_storage_size < 0) { + s64 size; + R_TRY(this->base_storage->GetSize(std::addressof(size))); + + this->base_storage_size = size; + } + + *out = this->base_storage_size; + return ResultSuccess(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); + + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, this->data_align); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, this->data_align); + const auto aligned_size = aligned_offset_end - aligned_offset; + + return this->base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp new file mode 100644 index 000000000..4cb698134 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp @@ -0,0 +1,28 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_istorage.hpp> + +namespace ams::fssystem { + + class AlignmentMatchingStorageImpl { + public: + static Result Read(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size); + static Result Write(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_allocator_utility.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_allocator_utility.hpp new file mode 100644 index 000000000..0ea327e6a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_allocator_utility.hpp @@ -0,0 +1,64 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem { + + using AllocateFunction = void *(*)(size_t size); + using DeallocateFunction = void (*)(void *ptr, size_t size); + + void InitializeAllocator(AllocateFunction allocate_func, DeallocateFunction deallocate_func); + + void *Allocate(size_t size); + void Deallocate(void *ptr, size_t size); + + template<typename T> + class StdAllocator : public std::allocator<T> { + public: + StdAllocator() { /* ... */ } + StdAllocator(const StdAllocator &) { /* ... */ } + template<class U> + StdAllocator(const StdAllocator<U> &) { /* ... */ } + + template<typename U> + struct rebind { + using other = StdAllocator<U>; + }; + + T *Allocate(size_t size, const T *hint = nullptr) { + return static_cast<T *>(::ams::fssystem::Allocate(sizeof(T) * size)); + } + + void Deallocate(T *ptr, size_t size) { + return ::ams::fssystem::Deallocate(ptr, sizeof(T) * size); + } + + ALWAYS_INLINE T *allocate(size_t size, const T *hint = nullptr) { return this->Allocate(size, hint); } + ALWAYS_INLINE void deallocate(T *ptr, size_t size) { return this->Deallocate(ptr, size); } + }; + + template<typename T> + std::shared_ptr<T> AllocateShared() { + return std::allocate_shared<T>(StdAllocator<T>{}); + } + + template<typename T, typename... Args> + std::shared_ptr<T> AllocateShared(Args &&... args) { + return std::allocate_shared<T>(StdAllocator<T>{}, std::forward<Args>(args)...); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp new file mode 100644 index 000000000..d01168e0b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp @@ -0,0 +1,330 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_substorage.hpp> + +namespace ams::fssystem { + + class BucketTree { + NON_COPYABLE(BucketTree); + NON_MOVEABLE(BucketTree); + public: + static constexpr u32 Magic = util::FourCC<'B','K','T','R'>::Code; + static constexpr u32 Version = 1; + + static constexpr size_t NodeSizeMin = 1_KB; + static constexpr size_t NodeSizeMax = 512_KB; + public: + class Visitor; + + struct Header { + u32 magic; + u32 version; + s32 entry_count; + s32 reserved; + + void Format(s32 entry_count); + Result Verify() const; + }; + static_assert(std::is_pod<Header>::value); + static_assert(sizeof(Header) == 0x10); + + struct NodeHeader { + s32 index; + s32 count; + s64 offset; + + Result Verify(s32 node_index, size_t node_size, size_t entry_size) const; + }; + static_assert(std::is_pod<NodeHeader>::value); + static_assert(sizeof(NodeHeader) == 0x10); + + class ContinuousReadingInfo { + private: + size_t read_size; + s32 skip_count; + bool done; + public: + constexpr ContinuousReadingInfo() : read_size(), skip_count(), done() { /* ... */ } + + constexpr void Reset() { this->read_size = 0; this->skip_count = 0; this->done = false; } + + constexpr void SetSkipCount(s32 count) { AMS_ASSERT(count >= 0); this->skip_count = count; } + constexpr s32 GetSkipCount() const { return this->skip_count; } + constexpr bool CheckNeedScan() { return (--this->skip_count) <= 0; } + + constexpr void Done() { this->read_size = 0; this->done = true; } + constexpr bool IsDone() const { return this->done; } + + constexpr void SetReadSize(size_t size) { this->read_size = size; } + constexpr size_t GetReadSize() const { return this->read_size; } + constexpr bool CanDo() const { return this->read_size > 0; } + }; + + using IAllocator = MemoryResource; + private: + class NodeBuffer { + NON_COPYABLE(NodeBuffer); + private: + IAllocator *allocator; + void *header; + public: + NodeBuffer() : allocator(), header() { /* ... */ } + + ~NodeBuffer() { + AMS_ASSERT(this->header == nullptr); + } + + NodeBuffer(NodeBuffer &&rhs) : allocator(rhs.allocator), header(rhs.allocator) { + rhs.allocator = nullptr; + rhs.header = nullptr; + } + + NodeBuffer &operator=(NodeBuffer &&rhs) { + if (this != std::addressof(rhs)) { + AMS_ASSERT(this->header == nullptr); + + this->allocator = rhs.allocator; + this->header = rhs.header; + + rhs.allocator = nullptr; + rhs.header = nullptr; + } + return *this; + } + + bool Allocate(IAllocator *allocator, size_t node_size) { + AMS_ASSERT(this->header == nullptr); + + this->allocator = allocator; + this->header = allocator->Allocate(node_size, sizeof(s64)); + + AMS_ASSERT(util::IsAligned(this->header, sizeof(s64))); + + return this->header != nullptr; + } + + void Free(size_t node_size) { + if (this->header) { + this->allocator->Deallocate(this->header, node_size); + this->header = nullptr; + } + this->allocator = nullptr; + } + + void FillSzero(size_t node_size) const { + if (this->header) { + std::memset(this->header, 0, node_size); + } + } + + NodeHeader *Get() const { + return reinterpret_cast<NodeHeader *>(this->header); + } + + NodeHeader *operator->() const { return this->Get(); } + + template<typename T> + T *Get() const { + static_assert(std::is_pod<T>::value); + static_assert(sizeof(T) == sizeof(NodeHeader)); + return reinterpret_cast<T *>(this->header); + } + + IAllocator *GetAllocator() const { + return this->allocator; + } + }; + private: + static constexpr s32 GetEntryCount(size_t node_size, size_t entry_size) { + return static_cast<s32>((node_size - sizeof(NodeHeader)) / entry_size); + } + + static constexpr s32 GetOffsetCount(size_t node_size) { + return static_cast<s32>((node_size - sizeof(NodeHeader)) / sizeof(s64)); + } + + static constexpr s32 GetEntrySetCount(size_t node_size, size_t entry_size, s32 entry_count) { + const s32 entry_count_per_node = GetEntryCount(node_size, entry_size); + return util::DivideUp(entry_count, entry_count_per_node); + } + + static constexpr s32 GetNodeL2Count(size_t node_size, size_t entry_size, s32 entry_count) { + const s32 offset_count_per_node = GetOffsetCount(node_size); + const s32 entry_set_count = GetEntrySetCount(node_size, entry_size, entry_count); + + if (entry_set_count <= offset_count_per_node) { + return 0; + } + + const s32 node_l2_count = util::DivideUp(entry_set_count, offset_count_per_node); + AMS_ABORT_UNLESS(node_l2_count <= offset_count_per_node); + + return util::DivideUp(entry_set_count - (offset_count_per_node - (node_l2_count - 1)), offset_count_per_node); + } + public: + static constexpr s64 QueryHeaderStorageSize() { return sizeof(Header); } + + static constexpr s64 QueryNodeStorageSize(size_t node_size, size_t entry_size, s32 entry_count) { + AMS_ASSERT(entry_size >= sizeof(s64)); + AMS_ASSERT(node_size >= entry_size + sizeof(NodeHeader)); + AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); + AMS_ASSERT(util::IsPowerOfTwo(node_size)); + AMS_ASSERT(entry_count >= 0); + + if (entry_count <= 0) { + return 0; + } + return (1 + GetNodeL2Count(node_size, entry_size, entry_count)) * static_cast<s64>(node_size); + } + + static constexpr s64 QueryEntryStorageSize(size_t node_size, size_t entry_size, s32 entry_count) { + AMS_ASSERT(entry_size >= sizeof(s64)); + AMS_ASSERT(node_size >= entry_size + sizeof(NodeHeader)); + AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); + AMS_ASSERT(util::IsPowerOfTwo(node_size)); + AMS_ASSERT(entry_count >= 0); + + if (entry_count <= 0) { + return 0; + } + return GetEntrySetCount(node_size, entry_size, entry_count) * static_cast<s64>(node_size); + } + private: + mutable fs::SubStorage node_storage; + mutable fs::SubStorage entry_storage; + NodeBuffer node_l1; + size_t node_size; + size_t entry_size; + s32 entry_count; + s32 offset_count; + s32 entry_set_count; + s64 start_offset; + s64 end_offset; + public: + BucketTree() : node_storage(), entry_storage(), node_l1(), node_size(), entry_size(), entry_count(), offset_count(), entry_set_count(), start_offset(), end_offset() { /* ... */ } + ~BucketTree() { this->Finalize(); } + + Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, size_t node_size, size_t entry_size, s32 entry_count); + void Initialize(size_t node_size, s64 end_offset); + void Finalize(); + + bool IsInitialized() const { return this->node_size > 0; } + bool IsEmpty() const { return this->entry_size == 0; } + + Result Find(Visitor *visitor, s64 virtual_address) const; + Result InvalidateCache(); + + s32 GetEntryCount() const { return this->entry_count; } + IAllocator *GetAllocator() const { return this->node_l1.GetAllocator(); } + + s64 GetStart() const { return this->start_offset; } + s64 GetEnd() const { return this->end_offset; } + s64 GetSize() const { return this->end_offset - this->start_offset; } + + bool Includes(s64 offset) const { + return this->start_offset <= offset && offset < this->end_offset; + } + + bool Includes(s64 offset, s64 size) const { + return size > 0 && this->start_offset <= offset && size <= this->end_offset - offset; + } + private: + template<typename EntryType> + struct ContinuousReadingParam { + s64 offset; + size_t size; + NodeHeader entry_set; + s32 entry_index; + EntryType entry; + }; + private: + template<typename EntryType> + Result ScanContinuousReading(ContinuousReadingInfo *out_info, const ContinuousReadingParam<EntryType> ¶m) const; + + bool IsExistL2() const { return this->offset_count < this->entry_set_count; } + bool IsExistOffsetL2OnL1() const { return this->IsExistL2() && this->node_l1->count < this->offset_count; } + + s64 GetEntrySetIndex(s32 node_index, s32 offset_index) const { + return (this->offset_count - this->node_l1->count) + (this->offset_count * node_index) + offset_index; + } + }; + + class BucketTree::Visitor { + NON_COPYABLE(Visitor); + NON_MOVEABLE(Visitor); + private: + friend class BucketTree; + + union EntrySetHeader { + NodeHeader header; + struct Info { + s32 index; + s32 count; + s64 end; + s64 start; + } info; + static_assert(std::is_pod<Info>::value); + }; + static_assert(std::is_pod<EntrySetHeader>::value); + private: + const BucketTree *tree; + void *entry; + s32 entry_index; + s32 entry_set_count; + EntrySetHeader entry_set; + public: + constexpr Visitor() : tree(), entry(), entry_index(-1), entry_set_count(), entry_set{} { /* ... */ } + ~Visitor() { + if (this->entry != nullptr) { + this->tree->GetAllocator()->Deallocate(this->entry, this->tree->entry_size); + this->tree = nullptr; + this->entry = nullptr; + } + } + + bool IsValid() const { return this->entry_index >= 0; } + bool CanMoveNext() const { return this->IsValid() && (this->entry_index + 1 < this->entry_set.info.count || this->entry_set.info.index + 1 < this->entry_set.info.count); } + bool CanMovePrevious() const { return this->IsValid() && (this->entry_index > 0 || this->entry_set.info.index > 0); } + + Result MoveNext(); + Result MovePrevious(); + + template<typename EntryType> + Result ScanContinuousReading(ContinuousReadingInfo *out_info, s64 offset, size_t size) const; + + const void *Get() const { AMS_ASSERT(this->IsValid()); return this->entry; } + + template<typename T> + const T *Get() const { AMS_ASSERT(this->IsValid()); return reinterpret_cast<const T *>(this->entry); } + + const BucketTree *GetTree() const { return this->tree; } + private: + Result Initialize(const BucketTree *tree); + + Result Find(s64 virtual_address); + + Result FindEntrySet(s32 *out_index, s64 virtual_address, s32 node_index); + Result FindEntrySetWithBuffer(s32 *out_index, s64 virtual_address, s32 node_index, char *buffer); + Result FindEntrySetWithoutBuffer(s32 *out_index, s64 virtual_address, s32 node_index); + + Result FindEntry(s64 virtual_address, s32 entry_set_index); + Result FindEntryWithBuffer(s64 virtual_address, s32 entry_set_index, char *buffer); + Result FindEntryWithoutBuffer(s64 virtual_address, s32 entry_set_index); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp new file mode 100644 index 000000000..1fcb6d903 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp @@ -0,0 +1,165 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere/fssystem/fssystem_pooled_buffer.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree_utils.hpp> + +namespace ams::fssystem { + + template<typename EntryType> + Result BucketTree::ScanContinuousReading(ContinuousReadingInfo *out_info, const ContinuousReadingParam<EntryType> ¶m) const { + static_assert(std::is_pod<ContinuousReadingParam<EntryType>>::value); + + /* Validate our preconditions. */ + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(out_info != nullptr); + AMS_ASSERT(this->entry_size == sizeof(EntryType)); + + /* Reset the output. */ + out_info->Reset(); + + /* If there's nothing to read, we're done. */ + R_SUCCEED_IF(param.size == 0); + + /* If we're reading a fragment, we're done. */ + R_SUCCEED_IF(param.entry.IsFragment()); + + /* Validate the first entry. */ + auto entry = param.entry; + auto cur_offset = param.offset; + R_UNLESS(entry.GetVirtualOffset() <= cur_offset, fs::ResultOutOfRange()); + + /* Create a pooled buffer for our scan. */ + PooledBuffer pool(this->node_size, 1); + char *buffer = nullptr; + + /* Read the node. */ + if (this->node_size <= pool.GetSize()) { + buffer = pool.GetBuffer(); + const auto ofs = param.entry_set.index * static_cast<s64>(this->node_size); + R_TRY(this->entry_storage.Read(ofs, buffer, this->node_size)); + } + + /* Calculate extents. */ + const auto end_offset = cur_offset + static_cast<s64>(param.size); + s64 phys_offset = entry.GetPhysicalOffset(); + + /* Start merge tracking. */ + s64 merge_size = 0; + s64 readable_size = 0; + bool merged = false; + + /* Iterate. */ + auto entry_index = param.entry_index; + for (const auto entry_count = param.entry_set.count; entry_index < entry_count; ++entry_index) { + /* If we're past the end, we're done. */ + if (end_offset <= cur_offset) { + break; + } + + /* Validate the entry offset. */ + const auto entry_offset = entry.GetVirtualOffset(); + R_UNLESS(entry_offset <= cur_offset, fs::ResultInvalidIndirectEntryOffset()); + + /* Get the next entry. */ + EntryType next_entry = {}; + s64 next_entry_offset; + + if (entry_index + 1 < entry_count) { + if (buffer != nullptr) { + const auto ofs = impl::GetBucketTreeEntryOffset(0, this->entry_size, entry_index + 1); + std::memcpy(std::addressof(next_entry), buffer + ofs, this->entry_size); + } else { + const auto ofs = impl::GetBucketTreeEntryOffset(param.entry_set.index, this->node_size, this->entry_size, entry_index + 1); + R_TRY(this->entry_storage.Read(ofs, std::addressof(next_entry), this->entry_size)); + } + + next_entry_offset = next_entry.GetVirtualOffset(); + R_UNLESS(this->Includes(next_entry_offset), fs::ResultInvalidIndirectEntryOffset()); + } else { + next_entry_offset = param.entry_set.offset; + } + + /* Validate the next entry offset. */ + R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset()); + + /* Determine the much data there is. */ + const auto data_size = next_entry_offset - cur_offset; + AMS_ASSERT(data_size > 0); + + /* Determine how much data we should read. */ + const auto remaining_size = end_offset - cur_offset; + const size_t read_size = static_cast<size_t>(std::min(data_size, remaining_size)); + AMS_ASSERT(read_size <= param.size); + + /* Update our merge tracking. */ + if (entry.IsFragment()) { + /* If we can't merge, stop looping. */ + if (EntryType::FragmentSizeMax <= read_size || remaining_size <= data_size) { + break; + } + + /* Otherwise, add the current size to the merge size. */ + merge_size += read_size; + } else { + /* If we can't merge, stop looping. */ + if (phys_offset != entry.GetPhysicalOffset()) { + break; + } + + /* Add the size to the readable amount. */ + readable_size += merge_size + read_size; + AMS_ASSERT(readable_size <= static_cast<s64>(param.size)); + + /* Update whether we've merged. */ + merged |= merge_size > 0; + merge_size = 0; + } + + /* Advance. */ + cur_offset += read_size; + AMS_ASSERT(cur_offset <= end_offset); + + phys_offset += next_entry_offset - entry_offset; + entry = next_entry; + } + + /* If we merged, set our readable size. */ + if (merged) { + out_info->SetReadSize(static_cast<size_t>(readable_size)); + } + out_info->SetSkipCount(entry_index - param.entry_index); + + return ResultSuccess(); + } + + template<typename EntryType> + Result BucketTree::Visitor::ScanContinuousReading(ContinuousReadingInfo *out_info, s64 offset, size_t size) const { + static_assert(std::is_pod<EntryType>::value); + AMS_ASSERT(this->IsValid()); + + /* Create our parameters. */ + ContinuousReadingParam<EntryType> param = { + offset, size, this->entry_set.header, this->entry_index + }; + std::memcpy(std::addressof(param.entry), this->entry, sizeof(EntryType)); + + /* Scan. */ + return this->tree->ScanContinuousReading<EntryType>(out_info, param); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_utils.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_utils.hpp new file mode 100644 index 000000000..576dc7d5a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_utils.hpp @@ -0,0 +1,85 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere/fssystem/fssystem_bucket_tree.hpp> + +namespace ams::fssystem::impl { + + class SafeValue { + public: + static ALWAYS_INLINE s64 GetInt64(const void *ptr) { + s64 value; + std::memcpy(std::addressof(value), ptr, sizeof(s64)); + return value; + } + + static ALWAYS_INLINE s64 GetInt64(const s64 *ptr) { + return GetInt64(static_cast<const void *>(ptr)); + } + + static ALWAYS_INLINE s64 GetInt64(const s64 &v) { + return GetInt64(std::addressof(v)); + } + + static ALWAYS_INLINE void SetInt64(void *dst, const void *src) { + std::memcpy(dst, src, sizeof(s64)); + } + + static ALWAYS_INLINE void SetInt64(void *dst, const s64 *src) { + return SetInt64(dst, static_cast<const void *>(src)); + } + + static ALWAYS_INLINE void SetInt64(void *dst, const s64 &v) { + return SetInt64(dst, std::addressof(v)); + } + }; + + template<typename IteratorType> + struct BucketTreeNode { + using Header = BucketTree::NodeHeader; + + Header header; + + s32 GetCount() const { return this->header.count; } + + void *GetArray() { return std::addressof(this->header) + 1; } + template<typename T> T *GetArray() { return reinterpret_cast<T *>(this->GetArray()); } + const void *GetArray() const { return std::addressof(this->header) + 1; } + template<typename T> const T *GetArray() const { return reinterpret_cast<const T *>(this->GetArray()); } + + s64 GetBeginOffset() const { return *this->GetArray<s64>(); } + s64 GetEndOffset() const { return this->header.offset; } + + IteratorType GetBegin() { return IteratorType(this->GetArray<s64>()); } + IteratorType GetEnd() { return IteratorType(this->GetArray<s64>()) + this->header.count; } + IteratorType GetBegin() const { return IteratorType(this->GetArray<s64>()); } + IteratorType GetEnd() const { return IteratorType(this->GetArray<s64>()) + this->header.count; } + + IteratorType GetBegin(size_t entry_size) { return IteratorType(this->GetArray(), entry_size); } + IteratorType GetEnd(size_t entry_size) { return IteratorType(this->GetArray(), entry_size) + this->header.count; } + IteratorType GetBegin(size_t entry_size) const { return IteratorType(this->GetArray(), entry_size); } + IteratorType GetEnd(size_t entry_size) const { return IteratorType(this->GetArray(), entry_size) + this->header.count; } + }; + + constexpr inline s64 GetBucketTreeEntryOffset(s64 entry_set_offset, size_t entry_size, s32 entry_index) { + return entry_set_offset + sizeof(BucketTree::NodeHeader) + entry_index * static_cast<s64>(entry_size); + } + + constexpr inline s64 GetBucketTreeEntryOffset(s32 entry_set_index, size_t node_size, size_t entry_size, s32 entry_index) { + return GetBucketTreeEntryOffset(entry_set_index * static_cast<s64>(node_size), entry_size, entry_index); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_crypto_configuration.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_crypto_configuration.hpp new file mode 100644 index 000000000..2b047ffc6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_crypto_configuration.hpp @@ -0,0 +1,34 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp> + +namespace ams::fssystem { + + const ::ams::fssystem::NcaCryptoConfiguration *GetNcaCryptoConfiguration(bool prod); + + void SetUpKekAccessKeys(bool prod); + + void InvalidateHardwareAesKey(); + + const u8 *GetAcidSignatureKeyModulus(bool prod, size_t key_generation); + const u8 *GetAcidSignatureKeyPublicExponent(); + + constexpr inline size_t AcidSignatureKeyModulusSize = NcaCryptoConfiguration::Rsa2048KeyModulusSize; + constexpr inline size_t AcidSignatureKeyPublicExponentSize = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_file_system_proxy_api.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_file_system_proxy_api.hpp new file mode 100644 index 000000000..ab4c55615 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_file_system_proxy_api.hpp @@ -0,0 +1,33 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssrv::fscreator { + + struct FileSystemCreatorInterfaces; + +} + +namespace ams::fssystem { + + /* TODO: This is kind of really a fs process function/tied into fs main. */ + /* This should be re-examined when FS is reimplemented. */ + void InitializeForFileSystemProxy(); + + const ::ams::fssrv::fscreator::FileSystemCreatorInterfaces *GetFileSystemCreatorInterfaces(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp new file mode 100644 index 000000000..0f35e1640 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp @@ -0,0 +1,163 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssystem/fssystem_bucket_tree.hpp> + +namespace ams::fssystem { + + class IndirectStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(IndirectStorage); + NON_MOVEABLE(IndirectStorage); + public: + static constexpr s32 StorageCount = 2; + static constexpr size_t NodeSize = 16_KB; + + using IAllocator = MemoryResource; + + struct Entry { + u8 virt_offset[sizeof(s64)]; + u8 phys_offset[sizeof(s64)]; + s32 storage_index; + + void SetVirtualOffset(const s64 &ofs) { + std::memcpy(this->virt_offset, std::addressof(ofs), sizeof(s64)); + } + + s64 GetVirtualOffset() const { + s64 offset; + std::memcpy(std::addressof(offset), this->virt_offset, sizeof(s64)); + return offset; + } + + void SetPhysicalOffset(const s64 &ofs) { + std::memcpy(this->phys_offset, std::addressof(ofs), sizeof(s64)); + } + + s64 GetPhysicalOffset() const { + s64 offset; + std::memcpy(std::addressof(offset), this->phys_offset, sizeof(s64)); + return offset; + } + }; + static_assert(std::is_pod<Entry>::value); + static_assert(sizeof(Entry) == 0x14); + + struct EntryData { + s64 virt_offset; + s64 phys_offset; + s32 storage_index; + + void Set(const Entry &entry) { + this->virt_offset = entry.GetVirtualOffset(); + this->phys_offset = entry.GetPhysicalOffset(); + this->storage_index = entry.storage_index; + } + }; + static_assert(std::is_pod<EntryData>::value); + private: + struct ContinuousReadingEntry { + static constexpr size_t FragmentSizeMax = 4_KB; + + IndirectStorage::Entry entry; + + s64 GetVirtualOffset() const { + return this->entry.GetVirtualOffset(); + } + + s64 GetPhysicalOffset() const { + return this->entry.GetPhysicalOffset(); + } + + bool IsFragment() const { + return this->entry.storage_index != 0; + } + }; + static_assert(std::is_pod<ContinuousReadingEntry>::value); + public: + static constexpr s64 QueryHeaderStorageSize() { + return BucketTree::QueryHeaderStorageSize(); + } + + static constexpr s64 QueryNodeStorageSize(s32 entry_count) { + return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count); + } + + static constexpr s64 QueryEntryStorageSize(s32 entry_count) { + return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count); + } + private: + BucketTree table; + fs::SubStorage data_storage[StorageCount]; + public: + IndirectStorage() : table(), data_storage() { /* ... */ } + virtual ~IndirectStorage() { this->Finalize(); } + + Result Initialize(IAllocator *allocator, fs::SubStorage table_storage); + void Finalize(); + + bool IsInitialized() const { return this->table.IsInitialized(); } + + Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count) { + return this->table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count); + } + + void SetStorage(s32 idx, fs::SubStorage storage) { + AMS_ASSERT(0 <= idx && idx < StorageCount); + this->data_storage[idx] = storage; + } + + template<typename T> + void SetStorage(s32 idx, T storage, s64 offset, s64 size) { + AMS_ASSERT(0 <= idx && idx < StorageCount); + this->data_storage[idx] = fs::SubStorage(storage, offset, size); + } + + Result GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size); + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + *out = this->table.GetSize(); + return ResultSuccess(); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fs::ResultUnsupportedOperationInIndirectStorageA(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInIndirectStorageB(); + } + protected: + BucketTree &GetEntryTable() { return this->table; } + + fs::SubStorage &GetDataStorage(s32 index) { + AMS_ASSERT(0 <= index && index < StorageCount); + return this->data_storage[index]; + } + + template<bool ContinuousCheck, typename F> + Result OperatePerEntry(s64 offset, s64 size, F func); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage_template_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage_template_impl.hpp new file mode 100644 index 000000000..01ddcc8b5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage_template_impl.hpp @@ -0,0 +1,113 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere/fssystem/fssystem_indirect_storage.hpp> + +namespace ams::fssystem { + + template<bool ContinuousCheck, typename F> + Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) { + /* Validate preconditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(size >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Succeed if there's nothing to operate on. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + + /* Find the offset in our tree. */ + BucketTree::Visitor visitor; + R_TRY(this->table.Find(std::addressof(visitor), offset)); + { + const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); + R_UNLESS(0 <= entry_offset && this->table.Includes(entry_offset), fs::ResultInvalidIndirectEntryOffset()); + } + + /* Prepare to operate in chunks. */ + auto cur_offset = offset; + const auto end_offset = offset + static_cast<s64>(size); + BucketTree::ContinuousReadingInfo cr_info; + + while (cur_offset < end_offset) { + /* Get the current entry. */ + const auto cur_entry = *visitor.Get<Entry>(); + + /* Get and validate the entry's offset. */ + const auto cur_entry_offset = cur_entry.GetVirtualOffset(); + R_UNLESS(cur_entry_offset <= cur_offset, fs::ResultInvalidIndirectEntryOffset()); + + /* Validate the storage index. */ + R_UNLESS(0 <= cur_entry.storage_index && cur_entry.storage_index < StorageCount, fs::ResultInvalidIndirectEntryStorageIndex()); + + /* If we need to check the continuous info, do so. */ + if constexpr (ContinuousCheck) { + /* Scan, if we need to. */ + if (cr_info.CheckNeedScan()) { + R_TRY(visitor.ScanContinuousReading<ContinuousReadingEntry>(std::addressof(cr_info), cur_offset, static_cast<size_t>(end_offset - cur_offset))); + } + + /* Process a base storage entry. */ + if (cr_info.CanDo()) { + R_UNLESS(cur_entry.storage_index == 0, fs::ResultInvalidIndirectEntryStorageIndex()); + + const auto data_offset = cur_offset - cur_entry_offset; + R_TRY(func(std::addressof(this->data_storage[0]), cur_entry.GetPhysicalOffset() + data_offset, cur_offset, static_cast<s64>(cr_info.GetReadSize()))); + } + } + + /* Get and validate the next entry offset. */ + s64 next_entry_offset; + if (visitor.CanMoveNext()) { + R_TRY(visitor.MoveNext()); + next_entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); + R_UNLESS(this->table.Includes(next_entry_offset), fs::ResultInvalidIndirectEntryOffset()); + } else { + next_entry_offset = this->table.GetEnd(); + } + R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset()); + + /* Get the offset of the entry in the data we read. */ + const auto data_offset = cur_offset - cur_entry_offset; + const auto data_size = (next_entry_offset - cur_entry_offset) - data_offset; + AMS_ASSERT(data_size > 0); + + /* Determine how much is left. */ + const auto remaining_size = end_offset - cur_offset; + const auto cur_size = std::min(remaining_size, data_size); + AMS_ASSERT(cur_size <= size); + + /* Operate, if we need to. */ + bool needs_operate; + if constexpr (!ContinuousCheck) { + needs_operate = true; + } else { + needs_operate = !cr_info.IsDone() || cur_entry.storage_index != 0; + } + + if (needs_operate) { + R_TRY(func(std::addressof(this->data_storage[cur_entry.storage_index]), cur_entry.GetPhysicalOffset() + data_offset, cur_offset, cur_size)); + } + + cur_offset += cur_size; + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp new file mode 100644 index 000000000..fdc9d15b9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp @@ -0,0 +1,74 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/fs_memory_storage.hpp> +#include <stratosphere/fs/impl/fs_newable.hpp> +#include <stratosphere/fssystem/fssystem_nca_header.hpp> +#include <stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp> + +namespace ams::fssystem { + + constexpr inline size_t IntegrityLayerCountRomFs = 7; + constexpr inline size_t IntegrityHashLayerBlockSize = 16_KB; + + class IntegrityRomFsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + private: + save::HierarchicalIntegrityVerificationStorage integrity_storage; + save::FileSystemBufferManagerSet buffers; + os::Mutex mutex; + Hash master_hash; + std::unique_ptr<fs::MemoryStorage> master_hash_storage; + public: + IntegrityRomFsStorage() : mutex(true) { /* ... */ } + virtual ~IntegrityRomFsStorage() override { this->Finalize(); } + + Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm); + void Finalize(); + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + return this->integrity_storage.Read(offset, buffer, size); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return this->integrity_storage.Write(offset, buffer, size); + } + + virtual Result SetSize(s64 size) override { return fs::ResultUnsupportedOperationInIntegrityRomFsStorageA(); } + + virtual Result GetSize(s64 *out) override { + return this->integrity_storage.GetSize(out); + } + + virtual Result Flush() override { + return this->integrity_storage.Flush(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + return this->integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + } + + Result Commit() { + return this->integrity_storage.Commit(); + } + + save::FileSystemBufferManagerSet *GetBuffers() { + return this->integrity_storage.GetBuffers(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp new file mode 100644 index 000000000..10460c363 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp @@ -0,0 +1,226 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/impl/fs_newable.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fssystem/fssystem_nca_header.hpp> +#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> + +namespace ams::fssystem { + + class AesCtrCounterExtendedStorage; + class IndirectStorage; + class SparseStorage; + + struct NcaCryptoConfiguration; + + using KeyGenerationFunction = void (*)(void *dst_key, size_t dst_key_size, const void *src_key, size_t src_key_size, s32 key_type, const NcaCryptoConfiguration &cfg); + using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, s32 key_type, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size); + + struct NcaCryptoConfiguration { + static constexpr size_t Rsa2048KeyModulusSize = crypto::Rsa2048PssSha256Verifier::ModulusSize; + static constexpr size_t Rsa2048KeyPublicExponentSize = crypto::Rsa2048PssSha256Verifier::MaximumExponentSize; + static constexpr size_t Rsa2048KeyPrivateExponentSize = Rsa2048KeyModulusSize; + + static constexpr size_t Aes128KeySize = crypto::AesEncryptor128::KeySize; + + static constexpr size_t Header1SignatureKeyGenerationMax = 1; + + static constexpr s32 KeyAreaEncryptionKeyIndexCount = 3; + static constexpr s32 HeaderEncryptionKeyCount = 2; + + static constexpr size_t KeyGenerationMax = 32; + + const u8 *header_1_sign_key_moduli[Header1SignatureKeyGenerationMax + 1]; + u8 header_1_sign_key_public_exponent[Rsa2048KeyPublicExponentSize]; + u8 key_area_encryption_key_source[KeyAreaEncryptionKeyIndexCount][Aes128KeySize]; + u8 header_encryption_key_source[Aes128KeySize]; + u8 header_encrypted_encryption_keys[HeaderEncryptionKeyCount][Aes128KeySize]; + KeyGenerationFunction generate_key; + DecryptAesCtrFunction decrypt_aes_ctr; + DecryptAesCtrFunction decrypt_aes_ctr_external; + bool is_plaintext_header_available; + }; + static_assert(std::is_pod<NcaCryptoConfiguration>::value); + + constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) { + return key_type < 0; + } + + constexpr inline s32 GetKeyTypeValue(u8 key_index, u8 key_generation) { + constexpr s32 InvalidKeyTypeValue = -1; + static_assert(IsInvalidKeyTypeValue(InvalidKeyTypeValue)); + + if (key_index >= NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount) { + return InvalidKeyTypeValue; + } + + return NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * key_generation + key_index; + } + + constexpr inline s32 KeyAreaEncryptionKeyCount = NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * NcaCryptoConfiguration::KeyGenerationMax; + + enum class KeyType : s32 { + NcaHeaderKey = KeyAreaEncryptionKeyCount + 0, + NcaExternalKey = KeyAreaEncryptionKeyCount + 1, + SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 2, + SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 3, + }; + + class NcaReader : public ::ams::fs::impl::Newable { + NON_COPYABLE(NcaReader); + NON_MOVEABLE(NcaReader); + private: + NcaHeader header; + u8 decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize]; + std::shared_ptr<fs::IStorage> shared_base_storage; + std::unique_ptr<fs::IStorage> header_storage; + fs::IStorage *body_storage; + u8 external_decryption_key[NcaCryptoConfiguration::Aes128KeySize]; + DecryptAesCtrFunction decrypt_aes_ctr; + DecryptAesCtrFunction decrypt_aes_ctr_external; + bool is_software_aes_prioritized; + NcaHeader::EncryptionType header_encryption_type; + public: + NcaReader(); + ~NcaReader(); + + Result Initialize(fs::IStorage *base_storage, const NcaCryptoConfiguration &crypto_cfg); + Result Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg); + + fs::IStorage *GetBodyStorage(); + u32 GetMagic() const; + NcaHeader::DistributionType GetDistributionType() const; + NcaHeader::ContentType GetContentType() const; + u8 GetKeyGeneration() const; + u8 GetKeyIndex() const; + u64 GetContentSize() const; + u64 GetProgramId() const; + u32 GetContentIndex() const; + u32 GetSdkAddonVersion() const; + void GetRightsId(u8 *dst, size_t dst_size) const; + bool HasFsInfo(s32 index) const; + s32 GetFsCount() const; + const Hash &GetFsHeaderHash(s32 index) const; + void GetFsHeaderHash(Hash *dst, s32 index) const; + void GetFsInfo(NcaHeader::FsInfo *dst, s32 index) const; + u64 GetFsOffset(s32 index) const; + u64 GetFsEndOffset(s32 index) const; + u64 GetFsSize(s32 index) const; + void GetEncryptedKey(void *dst, size_t size) const; + const void *GetDecryptionKey(s32 index) const; + bool HasValidInternalKey() const; + bool HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const; + bool IsSoftwareAesPrioritized() const; + void PrioritizeSoftwareAes(); + bool HasExternalDecryptionKey() const; + const void *GetExternalDecryptionKey() const; + void SetExternalDecryptionKey(const void *src, size_t size); + void GetRawData(void *dst, size_t dst_size) const; + DecryptAesCtrFunction GetExternalDecryptAesCtrFunction() const; + DecryptAesCtrFunction GetExternalDecryptAesCtrFunctionForExternalKey() const; + NcaHeader::EncryptionType GetEncryptionType() const; + Result ReadHeader(NcaFsHeader *dst, s32 index) const; + + Result VerifyHeaderSign2(const void *key, size_t key_size); + }; + + class NcaFsHeaderReader : public ::ams::fs::impl::Newable { + NON_COPYABLE(NcaFsHeaderReader); + NON_MOVEABLE(NcaFsHeaderReader); + private: + NcaFsHeader data; + s32 fs_index; + public: + NcaFsHeaderReader() : fs_index(-1) { + std::memset(std::addressof(this->data), 0, sizeof(this->data)); + } + + Result Initialize(const NcaReader &reader, s32 index); + bool IsInitialized() const { return this->fs_index >= 0; } + + NcaFsHeader &GetData() { return this->data; } + const NcaFsHeader &GetData() const { return this->data; } + void GetRawData(void *dst, size_t dst_size) const; + + NcaFsHeader::HashData &GetHashData(); + const NcaFsHeader::HashData &GetHashData() const; + u16 GetVersion() const; + s32 GetFsIndex() const; + NcaFsHeader::FsType GetFsType() const; + NcaFsHeader::HashType GetHashType() const; + NcaFsHeader::EncryptionType GetEncryptionType() const; + NcaPatchInfo &GetPatchInfo(); + const NcaPatchInfo &GetPatchInfo() const; + const NcaAesCtrUpperIv GetAesCtrUpperIv() const; + bool ExistsSparseLayer() const; + NcaSparseInfo &GetSparseInfo(); + const NcaSparseInfo &GetSparseInfo() const; + }; + + class NcaFileSystemDriver : public ::ams::fs::impl::Newable { + NON_COPYABLE(NcaFileSystemDriver); + NON_MOVEABLE(NcaFileSystemDriver); + public: + class StorageOption; + class StorageOptionWithHeaderReader; + private: + std::shared_ptr<NcaReader> original_reader; + std::shared_ptr<NcaReader> reader; + MemoryResource * const allocator; + fssystem::IBufferManager * const buffer_manager; + public: + static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index); + public: + NcaFileSystemDriver(std::shared_ptr<NcaReader> reader, MemoryResource *allocator, IBufferManager *buffer_manager) : original_reader(), reader(reader), allocator(allocator), buffer_manager(buffer_manager) { + AMS_ASSERT(this->reader != nullptr); + } + + NcaFileSystemDriver(std::shared_ptr<NcaReader> original_reader, std::shared_ptr<NcaReader> reader, MemoryResource *allocator, IBufferManager *buffer_manager) : original_reader(original_reader), reader(reader), allocator(allocator), buffer_manager(buffer_manager) { + AMS_ASSERT(this->reader != nullptr); + } + + Result OpenRawStorage(std::shared_ptr<fs::IStorage> *out, s32 fs_index); + + Result OpenStorage(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index); + Result OpenStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option); + + Result OpenStorage(std::shared_ptr<fs::IStorage> *out, s32 fs_index) { + NcaFsHeaderReader dummy; + return this->OpenStorage(out, std::addressof(dummy), fs_index); + } + + Result OpenDecryptableStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option, bool indirect_needed); + + private: + class BaseStorage; + + Result CreateBaseStorage(BaseStorage *out, StorageOption *option); + + Result CreateDecryptableStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage); + Result CreateAesXtsStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage); + Result CreateAesCtrStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage); + Result CreateAesCtrExStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage); + + Result CreateIndirectStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, std::unique_ptr<fs::IStorage> base_storage); + + Result CreateVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader); + Result CreateSha256Storage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader); + Result CreateIntegrityVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp new file mode 100644 index 000000000..5e96ea97d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp @@ -0,0 +1,150 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_substorage.hpp> +#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp> + +namespace ams::fssystem { + + class NcaFileSystemDriver::StorageOption { + private: + friend class NcaFileSystemDriver; + private: + const s32 fs_index; + NcaFsHeaderReader * const header_reader; + fs::IStorage *data_storage; + s64 data_storage_size; + fs::IStorage *aes_ctr_ex_table_storage; + AesCtrCounterExtendedStorage *aes_ctr_ex_storage_raw; + fs::IStorage *aes_ctr_ex_storage; + IndirectStorage *indirect_storage; + SparseStorage *sparse_storage; + public: + explicit StorageOption(NcaFsHeaderReader *reader) : fs_index(reader->GetFsIndex()), header_reader(reader), data_storage(), data_storage_size(), aes_ctr_ex_table_storage(), aes_ctr_ex_storage_raw(), aes_ctr_ex_storage(), indirect_storage(), sparse_storage() { + AMS_ASSERT(this->header_reader != nullptr); + } + + StorageOption(NcaFsHeaderReader *reader, s32 index) : fs_index(index), header_reader(reader), data_storage(), data_storage_size(), aes_ctr_ex_table_storage(), aes_ctr_ex_storage_raw(), aes_ctr_ex_storage(), indirect_storage(), sparse_storage() { + AMS_ASSERT(this->header_reader != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + } + + s32 GetFsIndex() const { return this->fs_index; } + NcaFsHeaderReader &GetHeaderReader() { return *this->header_reader; } + const NcaFsHeaderReader &GetHeaderReader() const { return *this->header_reader; } + fs::SubStorage GetDataStorage() const { return fs::SubStorage(this->data_storage, 0, this->data_storage_size); } + fs::IStorage *GetAesCtrExTableStorage() const { return this->aes_ctr_ex_table_storage; } + fs::IStorage *GetAesCtrExStorage() const { return this->aes_ctr_ex_storage; } + AesCtrCounterExtendedStorage *GetAesCtrExStorageRaw() const { return this->aes_ctr_ex_storage_raw; } + IndirectStorage *GetIndirectStorage() const { return this->indirect_storage; } + SparseStorage *GetSparseStorage() const { return this->sparse_storage; } + private: + void SetDataStorage(fs::IStorage *storage, s64 size) { + AMS_ASSERT(storage != nullptr); + AMS_ASSERT(size >= 0); + this->data_storage = storage; + this->data_storage_size = size; + } + + void SetAesCtrExTableStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); this->aes_ctr_ex_table_storage = storage; } + void SetAesCtrExStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); this->aes_ctr_ex_storage = storage; } + void SetAesCtrExStorageRaw(AesCtrCounterExtendedStorage *storage) { AMS_ASSERT(storage != nullptr); this->aes_ctr_ex_storage_raw = storage; } + void SetIndirectStorage(IndirectStorage *storage) { AMS_ASSERT(storage != nullptr); this->indirect_storage = storage; } + void SetSparseStorage(SparseStorage *storage) { AMS_ASSERT(storage != nullptr); this->sparse_storage = storage; } + }; + + class NcaFileSystemDriver::StorageOptionWithHeaderReader : public NcaFileSystemDriver::StorageOption { + private: + NcaFsHeaderReader header_reader_data; + public: + explicit StorageOptionWithHeaderReader(s32 index) : StorageOption(std::addressof(header_reader_data), index) { /* ... */ } + }; + + class NcaFileSystemDriver::BaseStorage { + private: + std::unique_ptr<fs::IStorage> storage; + fs::SubStorage sub_storage; + s64 storage_offset; + NcaAesCtrUpperIv aes_ctr_upper_iv; + public: + BaseStorage() : storage(), sub_storage(), storage_offset(0) { + this->aes_ctr_upper_iv.value = 0; + } + + explicit BaseStorage(const fs::SubStorage &ss) : storage(), sub_storage(ss), storage_offset(0) { + this->aes_ctr_upper_iv.value = 0; + } + + template<typename T> + BaseStorage(T s, s64 offset, s64 size) : storage(), sub_storage(s, offset, size), storage_offset(0) { + this->aes_ctr_upper_iv.value = 0; + } + + void SetStorage(std::unique_ptr<fs::IStorage> &&storage) { + this->storage = std::move(storage); + } + + template<typename T> + void SetStorage(T storage, s64 offset, s64 size) { + this->sub_storage = fs::SubStorage(storage, offset, size); + } + + std::unique_ptr<fs::IStorage> MakeStorage() { + if (this->storage != nullptr) { + return std::move(this->storage); + } + return std::make_unique<fs::SubStorage>(this->sub_storage); + } + + std::unique_ptr<fs::IStorage> GetStorage() { + return std::move(this->storage); + } + + Result GetSubStorage(fs::SubStorage *out, s64 offset, s64 size) { + s64 storage_size = 0; + + if (this->storage != nullptr) { + R_TRY(this->storage->GetSize(std::addressof(storage_size))); + R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); + *out = fs::SubStorage(this->storage.get(), offset, size); + } else { + R_TRY(this->sub_storage.GetSize(std::addressof(storage_size))); + R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); + *out = fs::SubStorage(std::addressof(this->sub_storage), offset, size); + } + + return ResultSuccess(); + } + + void SetStorageOffset(s64 offset) { + this->storage_offset = offset; + } + + s64 GetStorageOffset() const { + return this->storage_offset; + } + + void SetAesCtrUpperIv(NcaAesCtrUpperIv v) { + this->aes_ctr_upper_iv = v; + } + + const NcaAesCtrUpperIv GetAesCtrUpperIv() const { + return this->aes_ctr_upper_iv; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp new file mode 100644 index 000000000..e949dc976 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp @@ -0,0 +1,275 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem { + + struct Hash { + static constexpr size_t Size = crypto::Sha256Generator::HashSize; + u8 value[Size]; + }; + static_assert(sizeof(Hash) == Hash::Size); + static_assert(std::is_pod<Hash>::value); + + using NcaDigest = Hash; + + struct NcaHeader { + enum class ContentType : u8 { + Program = 0, + Meta = 1, + Control = 2, + Manual = 3, + Data = 4, + PublicData = 5, + + Start = Program, + End = PublicData, + }; + + enum class DistributionType : u8 { + Download = 0, + GameCard = 1, + + Start = Download, + End = GameCard, + }; + + enum class EncryptionType : u8 { + Auto = 0, + None = 1, + }; + + enum DecryptionKey { + DecryptionKey_AesXts = 0, + DecryptionKey_AesXts1 = DecryptionKey_AesXts, + DecryptionKey_AesXts2 = 1, + DecryptionKey_AesCtr = 2, + DecryptionKey_AesCtrEx = 3, + DecryptionKey_AesCtrHw = 4, + DecryptionKey_Count, + }; + + struct FsInfo { + u32 start_sector; + u32 end_sector; + u32 hash_sectors; + u32 reserved; + }; + static_assert(sizeof(FsInfo) == 0x10); + static_assert(std::is_pod<FsInfo>::value); + + static constexpr u32 Magic0 = util::FourCC<'N','C','A','0'>::Code; + static constexpr u32 Magic1 = util::FourCC<'N','C','A','1'>::Code; + static constexpr u32 Magic2 = util::FourCC<'N','C','A','2'>::Code; + static constexpr u32 Magic3 = util::FourCC<'N','C','A','3'>::Code; + + static constexpr u32 Magic = Magic3; + + static constexpr size_t Size = 1_KB; + static constexpr s32 FsCountMax = 4; + static constexpr size_t HeaderSignCount = 2; + static constexpr size_t HeaderSignSize = 0x100; + static constexpr size_t EncryptedKeyAreaSize = 0x100; + static constexpr size_t SectorSize = 0x200; + static constexpr size_t SectorShift = 9; + static constexpr size_t RightsIdSize = 0x10; + static constexpr size_t XtsBlockSize = 0x200; + static constexpr size_t CtrBlockSize = 0x10; + + static_assert(SectorSize == (1 << SectorShift)); + + /* Data members. */ + u8 header_sign_1[HeaderSignSize]; + u8 header_sign_2[HeaderSignSize]; + u32 magic; + DistributionType distribution_type; + ContentType content_type; + u8 key_generation; + u8 key_index; + u64 content_size; + u64 program_id; + u32 content_index; + u32 sdk_addon_version; + u8 key_generation_2; + u8 header1_signature_key_generation; + u8 reserved_222[2]; + u32 reserved_224[3]; + u8 rights_id[RightsIdSize]; + FsInfo fs_info[FsCountMax]; + Hash fs_header_hash[FsCountMax]; + u8 encrypted_key_area[EncryptedKeyAreaSize]; + + static constexpr u64 SectorToByte(u32 sector) { + return static_cast<u64>(sector) << SectorShift; + } + + static constexpr u32 ByteToSector(u64 byte) { + return static_cast<u32>(byte >> SectorShift); + } + + u8 GetProperKeyGeneration() const; + }; + static_assert(sizeof(NcaHeader) == NcaHeader::Size); + static_assert(std::is_pod<NcaHeader>::value); + + struct NcaBucketInfo { + static constexpr size_t HeaderSize = 0x10; + s64 offset; + s64 size; + u8 header[HeaderSize]; + }; + static_assert(std::is_pod<NcaBucketInfo>::value); + + struct NcaPatchInfo { + static constexpr size_t Size = 0x40; + static constexpr size_t Offset = 0x100; + + s64 indirect_offset; + s64 indirect_size; + u8 indirect_header[NcaBucketInfo::HeaderSize]; + s64 aes_ctr_ex_offset; + s64 aes_ctr_ex_size; + u8 aes_ctr_ex_header[NcaBucketInfo::HeaderSize]; + + bool HasIndirectTable() const; + bool HasAesCtrExTable() const; + }; + static_assert(std::is_pod<NcaPatchInfo>::value); + + union NcaAesCtrUpperIv { + u64 value; + struct { + u32 generation; + u32 secure_value; + } part; + }; + static_assert(std::is_pod<NcaAesCtrUpperIv>::value); + + struct NcaSparseInfo { + NcaBucketInfo bucket; + s64 physical_offset; + u16 generation; + u8 reserved[6]; + + s64 GetPhysicalSize() const { + return this->bucket.offset + this->bucket.size; + } + + u32 GetGeneration() const { + return static_cast<u32>(this->generation) << 16; + } + + const NcaAesCtrUpperIv MakeAesCtrUpperIv(NcaAesCtrUpperIv upper_iv) const { + NcaAesCtrUpperIv sparse_upper_iv = upper_iv; + sparse_upper_iv.part.generation = this->GetGeneration(); + return sparse_upper_iv; + } + }; + static_assert(std::is_pod<NcaSparseInfo>::value); + + struct NcaFsHeader { + static constexpr size_t Size = 0x200; + static constexpr size_t HashDataOffset = 0x8; + + struct Region { + s64 offset; + s64 size; + }; + static_assert(std::is_pod<Region>::value); + + enum class FsType : u8 { + RomFs = 0, + PartitionFs = 1, + }; + + enum class EncryptionType : u8 { + Auto = 0, + None = 1, + AesXts = 2, + AesCtr = 3, + AesCtrEx = 4, + }; + + enum class HashType : u8 { + Auto = 0, + None = 1, + HierarchicalSha256Hash = 2, + HierarchicalIntegrityHash = 3, + }; + + union HashData { + struct HierarchicalSha256Data { + static constexpr size_t HashLayerCountMax = 5; + static const size_t MasterHashOffset; + + Hash fs_data_master_hash; + s32 hash_block_size; + s32 hash_layer_count; + Region hash_layer_region[HashLayerCountMax]; + } hierarchical_sha256_data; + static_assert(std::is_pod<HierarchicalSha256Data>::value); + + struct IntegrityMetaInfo { + static const size_t MasterHashOffset; + + u32 magic; + u32 version; + u32 master_hash_size; + + struct LevelHashInfo { + u32 max_layers; + + struct HierarchicalIntegrityVerificationLevelInformation { + static constexpr size_t IntegrityMaxLayerCount = 7; + s64 offset; + s64 size; + s32 block_order; + u8 reserved[4]; + } info[HierarchicalIntegrityVerificationLevelInformation::IntegrityMaxLayerCount - 1]; + + struct SignatureSalt { + static constexpr size_t Size = 0x20; + u8 value[Size]; + } seed; + } level_hash_info; + + Hash master_hash; + } integrity_meta_info; + static_assert(std::is_pod<IntegrityMetaInfo>::value); + + u8 padding[NcaPatchInfo::Offset - HashDataOffset]; + }; + + u16 version; + FsType fs_type; + HashType hash_type; + EncryptionType encryption_type; + u8 reserved[3]; + HashData hash_data; + NcaPatchInfo patch_info; + NcaAesCtrUpperIv aes_ctr_upper_iv; + NcaSparseInfo sparse_info; + u8 pad[0x88]; + }; + static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size); + static_assert(std::is_pod<NcaFsHeader>::value); + static_assert(offsetof(NcaFsHeader, patch_info) == NcaPatchInfo::Offset); + + inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = offsetof(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash); + inline constexpr const size_t NcaFsHeader::HashData::IntegrityMetaInfo::MasterHashOffset = offsetof(NcaFsHeader, hash_data.integrity_meta_info.master_hash); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_sparse_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_sparse_storage.hpp new file mode 100644 index 000000000..276b5f030 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_sparse_storage.hpp @@ -0,0 +1,97 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fssystem/fssystem_indirect_storage.hpp> + +namespace ams::fssystem { + + class SparseStorage : public IndirectStorage { + NON_COPYABLE(SparseStorage); + NON_MOVEABLE(SparseStorage); + private: + class ZeroStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + public: + ZeroStorage() { /* ... */ } + virtual ~ZeroStorage() { /* ... */ } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + AMS_ASSERT(offset >= 0); + AMS_ASSERT(buffer != nullptr || size == 0); + if (size > 0) { + std::memset(buffer, 0, size); + } + return ResultSuccess(); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + return ResultSuccess(); + } + + virtual Result GetSize(s64 *out) override { + AMS_ASSERT(out != nullptr); + *out = std::numeric_limits<s64>::max(); + return ResultSuccess(); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fs::ResultUnsupportedOperationInZeroStorageA(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInZeroStorageB(); + } + }; + private: + ZeroStorage zero_storage; + public: + SparseStorage() : IndirectStorage(), zero_storage() { /* ... */ } + virtual ~SparseStorage() { /* ... */ } + + using IndirectStorage::Initialize; + + void Initialize(s64 end_offset) { + this->GetEntryTable().Initialize(NodeSize, end_offset); + this->SetZeroStorage(); + } + + void SetDataStorage(fs::SubStorage storage) { + AMS_ASSERT(this->IsInitialized()); + + this->SetStorage(0, storage); + this->SetZeroStorage(); + } + + template<typename T> + void SetDataStorage(T storage, s64 offset, s64 size) { + AMS_ASSERT(this->IsInitialized()); + + this->SetStorage(0, storage, offset, size); + this->SetZeroStorage(); + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + private: + void SetZeroStorage() { + return this->SetStorage(1, std::addressof(this->zero_storage), 0, std::numeric_limits<s64>::max()); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_speed_emulation_configuration.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_speed_emulation_configuration.hpp new file mode 100644 index 000000000..3b6a84704 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_speed_emulation_configuration.hpp @@ -0,0 +1,28 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/fs/fs_speed_emulation.hpp> + +namespace ams::fssystem { + + class SpeedEmulationConfiguration { + public: + static void SetSpeedEmulationMode(::ams::fs::SpeedEmulationMode mode); + static ::ams::fs::SpeedEmulationMode GetSpeedEmulationMode(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp new file mode 100644 index 000000000..e708fb8a9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp @@ -0,0 +1,170 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> +#include <stratosphere/fs/fs_storage_type.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/fs_memory_management.hpp> +#include <stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp> +#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp> + +namespace ams::fssystem::save { + + constexpr inline size_t IntegrityMinLayerCount = 2; + constexpr inline size_t IntegrityMaxLayerCount = 7; + constexpr inline size_t IntegrityLayerCountSave = 5; + constexpr inline size_t IntegrityLayerCountSaveDataMeta = 4; + + struct FileSystemBufferManagerSet { + IBufferManager *buffers[IntegrityMaxLayerCount]; + }; + static_assert(std::is_pod<FileSystemBufferManagerSet>::value); + + class BlockCacheBufferedStorage : public ::ams::fs::IStorage { + NON_COPYABLE(BlockCacheBufferedStorage); + NON_MOVEABLE(BlockCacheBufferedStorage); + public: + static constexpr size_t DefaultMaxCacheEntryCount = 24; + private: + using MemoryRange = std::pair<uintptr_t, size_t>; + using CacheIndex = s32; + + struct CacheEntry { + size_t size; + bool is_valid; + bool is_write_back; + bool is_cached; + bool is_flushing; + s64 offset; + IBufferManager::CacheHandle handle; + uintptr_t memory_address; + size_t memory_size; + }; + static_assert(std::is_pod<CacheEntry>::value); + + enum Flag : s32 { + Flag_KeepBurstMode = (1 << 8), + Flag_RealData = (1 << 10), + }; + private: + IBufferManager *buffer_manager; + os::Mutex *mutex; + std::unique_ptr<CacheEntry[], ::ams::fs::impl::Deleter> entries; + IStorage *data_storage; + Result last_result; + s64 data_size; + size_t verification_block_size; + size_t verification_block_shift; + CacheIndex invalidate_index; + s32 max_cache_entry_count; + s32 flags; + s32 buffer_level; + fs::StorageType storage_type; + public: + BlockCacheBufferedStorage(); + virtual ~BlockCacheBufferedStorage() override; + + Result Initialize(IBufferManager *bm, os::Mutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, fs::StorageType storage_type); + void Finalize(); + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result Write(s64 offset, const void *buffer, size_t size) override; + + virtual Result SetSize(s64 size) override { return fs::ResultUnsupportedOperationInBlockCacheBufferedStorageA(); } + virtual Result GetSize(s64 *out) override; + + virtual Result Flush() override; + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + using IStorage::OperateRange; + + Result Commit(); + Result OnRollback(); + + bool IsEnabledKeepBurstMode() const { + return (this->flags & Flag_KeepBurstMode) != 0; + } + + bool IsRealDataCache() const { + return (this->flags & Flag_RealData) != 0; + } + + void SetKeepBurstMode(bool en) { + if (en) { + this->flags |= Flag_KeepBurstMode; + } else { + this->flags &= ~Flag_KeepBurstMode; + } + } + + void SetRealDataCache(bool en) { + if (en) { + this->flags |= Flag_RealData; + } else { + this->flags &= ~Flag_RealData; + } + } + private: + s32 GetMaxCacheEntryCount() const { + return this->max_cache_entry_count; + } + + Result ClearImpl(s64 offset, s64 size); + Result ClearSignatureImpl(s64 offset, s64 size); + Result InvalidateCacheImpl(s64 offset, s64 size); + Result QueryRangeImpl(void *dst, size_t dst_size, s64 offset, s64 size); + + bool ExistsRedundantCacheEntry(const CacheEntry &entry) const; + + Result GetAssociateBuffer(MemoryRange *out_range, CacheEntry *out_entry, s64 offset, size_t ideal_size, bool is_allocate_for_write); + + void DestroyBuffer(CacheEntry *entry, const MemoryRange &range); + + Result StoreAssociateBuffer(CacheIndex *out, const MemoryRange &range, const CacheEntry &entry); + Result StoreAssociateBuffer(const MemoryRange &range, const CacheEntry &entry) { + CacheIndex dummy; + return this->StoreAssociateBuffer(std::addressof(dummy), range, entry); + } + + Result StoreOrDestroyBuffer(const MemoryRange &range, CacheEntry *entry) { + AMS_ASSERT(entry != nullptr); + + auto buf_guard = SCOPE_GUARD { this->DestroyBuffer(entry, range); }; + + R_TRY(this->StoreAssociateBuffer(range, *entry)); + + buf_guard.Cancel(); + return ResultSuccess(); + } + + Result FlushCacheEntry(CacheIndex index, bool invalidate); + Result FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate); + void InvalidateRangeCacheEntries(s64 offset, s64 size); + + Result FlushAllCacheEntries(); + Result InvalidateAllCacheEntries(); + Result ControlDirtiness(); + + Result UpdateLastResult(Result result); + + Result ReadHeadCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 *offset, s64 *aligned_offset, s64 aligned_offset_end, char **buffer, size_t *size); + Result ReadTailCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 offset, s64 aligned_offset, s64 *aligned_offset_end, char *buffer, size_t *size); + + Result BulkRead(s64 offset, void *buffer, size_t size, MemoryRange *range_head, MemoryRange *range_tail, CacheEntry *entry_head, CacheEntry *entry_tail, bool head_cache_needed, bool tail_cache_needed); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_buffered_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_buffered_storage.hpp new file mode 100644 index 000000000..eb30a8a7e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_buffered_storage.hpp @@ -0,0 +1,80 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/fs_substorage.hpp> +#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> + +namespace ams::fssystem::save { + + class BufferedStorage : public ::ams::fs::IStorage { + NON_COPYABLE(BufferedStorage); + NON_MOVEABLE(BufferedStorage); + private: + class Cache; + class UniqueCache; + class SharedCache; + private: + fs::SubStorage base_storage; + IBufferManager *buffer_manager; + size_t block_size; + s64 base_storage_size; + std::unique_ptr<Cache[]> caches; + s32 cache_count; + Cache *next_acquire_cache; + Cache *next_fetch_cache; + os::Mutex mutex; + bool bulk_read_enabled; + public: + BufferedStorage(); + virtual ~BufferedStorage(); + + Result Initialize(fs::SubStorage base_storage, IBufferManager *buffer_manager, size_t block_size, s32 buffer_count); + void Finalize(); + + bool IsInitialized() const { return this->caches != nullptr; } + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result Write(s64 offset, const void *buffer, size_t size) override; + + virtual Result GetSize(s64 *out) override; + virtual Result SetSize(s64 size) override; + + virtual Result Flush() override; + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + using IStorage::OperateRange; + + void InvalidateCaches(); + + IBufferManager *GetBufferManager() const { return this->buffer_manager; } + + void EnableBulkRead() { this->bulk_read_enabled = true; } + private: + Result PrepareAllocation(); + Result ControlDirtiness(); + Result ReadCore(s64 offset, void *buffer, size_t size); + + bool ReadHeadCache(s64 *offset, void *buffer, size_t *size, s64 *buffer_offset); + bool ReadTailCache(s64 offset, void *buffer, size_t *size, s64 buffer_offset); + + Result BulkRead(s64 offset, void *buffer, size_t size, bool head_cache_needed, bool tail_cache_needed); + + Result WriteCore(s64 offset, const void *buffer, size_t size); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp new file mode 100644 index 000000000..1140e3641 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp @@ -0,0 +1,205 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/fs_substorage.hpp> +#include <stratosphere/fs/fs_storage_type.hpp> +#include <stratosphere/fssystem/save/fssystem_i_save_file.hpp> +#include <stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp> +#include <stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp> + +namespace ams::fssystem::save { + + struct HierarchicalIntegrityVerificationLevelInformation { + fs::Int64 offset; + fs::Int64 size; + s32 block_order; + u8 reserved[4]; + }; + static_assert(std::is_pod<HierarchicalIntegrityVerificationLevelInformation>::value); + static_assert(sizeof(HierarchicalIntegrityVerificationLevelInformation) == 0x18); + static_assert(alignof(HierarchicalIntegrityVerificationLevelInformation) == 0x4); + + struct HierarchicalIntegrityVerificationInformation { + u32 max_layers; + HierarchicalIntegrityVerificationLevelInformation info[IntegrityMaxLayerCount - 1]; + fs::HashSalt seed; + + s64 GetLayeredHashSize() const { + return this->info[this->max_layers - 2].offset; + } + + s64 GetDataOffset() const { + return this->info[this->max_layers - 2].offset; + } + + s64 GetDataSize() const { + return this->info[this->max_layers - 2].size; + } + }; + static_assert(std::is_pod<HierarchicalIntegrityVerificationInformation>::value); + + struct HierarchicalIntegrityVerificationMetaInformation { + u32 magic; + u32 version; + u32 master_hash_size; + HierarchicalIntegrityVerificationInformation level_hash_info; + + /* TODO: Format */ + }; + static_assert(std::is_pod<HierarchicalIntegrityVerificationMetaInformation>::value); + + struct HierarchicalIntegrityVerificationSizeSet { + s64 control_size; + s64 master_hash_size; + s64 layered_hash_sizes[IntegrityMaxLayerCount - 1]; + }; + static_assert(std::is_pod<HierarchicalIntegrityVerificationSizeSet>::value); + + class HierarchicalIntegrityVerificationStorageControlArea { + NON_COPYABLE(HierarchicalIntegrityVerificationStorageControlArea); + NON_MOVEABLE(HierarchicalIntegrityVerificationStorageControlArea); + public: + static constexpr size_t HashSize = crypto::Sha256Generator::HashSize; + + struct InputParam { + size_t level_block_size[IntegrityMaxLayerCount - 1]; + }; + static_assert(std::is_pod<InputParam>::value); + private: + fs::SubStorage storage; + HierarchicalIntegrityVerificationMetaInformation meta; + public: + static Result QuerySize(HierarchicalIntegrityVerificationSizeSet *out, const InputParam &input_param, s32 layer_count, s64 data_size); + /* TODO Format */ + static Result Expand(fs::SubStorage meta_storage, const HierarchicalIntegrityVerificationMetaInformation &meta); + public: + HierarchicalIntegrityVerificationStorageControlArea() { /* ... */ } + + Result Initialize(fs::SubStorage meta_storage); + void Finalize(); + + u32 GetMasterHashSize() const { return this->meta.master_hash_size; } + void GetLevelHashInfo(HierarchicalIntegrityVerificationInformation *out) { + AMS_ASSERT(out != nullptr); + *out = this->meta.level_hash_info; + } + }; + + class HierarchicalIntegrityVerificationStorage : public ::ams::fs::IStorage { + NON_COPYABLE(HierarchicalIntegrityVerificationStorage); + NON_MOVEABLE(HierarchicalIntegrityVerificationStorage); + private: + friend class HierarchicalIntegrityVerificationMetaInformation; + protected: + static constexpr s64 HashSize = crypto::Sha256Generator::HashSize; + static constexpr size_t MaxLayers = IntegrityMaxLayerCount; + public: + using GenerateRandomFunction = void (*)(void *dst, size_t size); + + class HierarchicalStorageInformation { + public: + enum { + MasterStorage = 0, + Layer1Storage = 1, + Layer2Storage = 2, + Layer3Storage = 3, + Layer4Storage = 4, + Layer5Storage = 5, + DataStorage = 6, + }; + private: + fs::SubStorage storages[DataStorage + 1]; + public: + void SetMasterHashStorage(fs::SubStorage s) { this->storages[MasterStorage] = s; } + void SetLayer1HashStorage(fs::SubStorage s) { this->storages[Layer1Storage] = s; } + void SetLayer2HashStorage(fs::SubStorage s) { this->storages[Layer2Storage] = s; } + void SetLayer3HashStorage(fs::SubStorage s) { this->storages[Layer3Storage] = s; } + void SetLayer4HashStorage(fs::SubStorage s) { this->storages[Layer4Storage] = s; } + void SetLayer5HashStorage(fs::SubStorage s) { this->storages[Layer5Storage] = s; } + void SetDataStorage(fs::SubStorage s) { this->storages[DataStorage] = s; } + + fs::SubStorage &operator[](s32 index) { + AMS_ASSERT(MasterStorage <= index && index <= DataStorage); + return this->storages[index]; + } + }; + private: + static GenerateRandomFunction s_generate_random; + + static void SetGenerateRandomFunction(GenerateRandomFunction func) { + s_generate_random = func; + } + private: + FileSystemBufferManagerSet *buffers; + os::Mutex *mutex; + IntegrityVerificationStorage verify_storages[MaxLayers - 1]; + BlockCacheBufferedStorage buffer_storages[MaxLayers - 1]; + s64 data_size; + s32 max_layers; + bool is_written_for_rollback; + public: + HierarchicalIntegrityVerificationStorage() : buffers(nullptr), mutex(nullptr), data_size(-1), is_written_for_rollback(false) { /* ... */ } + virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); } + + Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, os::Mutex *mtx, fs::StorageType storage_type); + void Finalize(); + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result Write(s64 offset, const void *buffer, size_t size) override; + + virtual Result SetSize(s64 size) override { return fs::ResultUnsupportedOperationInHierarchicalIntegrityVerificationStorageA(); } + virtual Result GetSize(s64 *out) override; + + virtual Result Flush() override; + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + using IStorage::OperateRange; + + Result Commit(); + Result OnRollback(); + + bool IsInitialized() const { + return this->data_size >= 0; + } + + bool IsWrittenForRollback() const { + return this->is_written_for_rollback; + } + + FileSystemBufferManagerSet *GetBuffers() { + return this->buffers; + } + + void GetParameters(HierarchicalIntegrityVerificationStorageControlArea::InputParam *out) const { + AMS_ASSERT(out != nullptr); + for (auto level = 0; level <= this->max_layers - 2; ++level) { + out->level_block_size[level] = static_cast<size_t>(this->verify_storages[level].GetBlockSize()); + } + } + + s64 GetL1HashVerificationBlockSize() const { + return this->verify_storages[this->max_layers - 2].GetBlockSize(); + } + + fs::SubStorage GetL1HashStorage() { + return fs::SubStorage(std::addressof(this->buffer_storages[this->max_layers - 3]), 0, util::DivideUp(this->data_size, this->GetL1HashVerificationBlockSize())); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file.hpp new file mode 100644 index 000000000..69a4f5ec3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem::save { + + /* TODO */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp new file mode 100644 index 000000000..69a4f5ec3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::fssystem::save { + + /* TODO */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp new file mode 100644 index 000000000..13871b9fc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp @@ -0,0 +1,95 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/os.hpp> +#include <stratosphere/fs/fs_istorage.hpp> +#include <stratosphere/fs/fs_substorage.hpp> +#include <stratosphere/fs/fs_storage_type.hpp> +#include <stratosphere/fs/fs_save_data_types.hpp> +#include <stratosphere/fssystem/save/fssystem_save_types.hpp> +#include <stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp> +#include <stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp> + +namespace ams::fssystem::save { + + class IntegrityVerificationStorage : public ::ams::fs::IStorage { + NON_COPYABLE(IntegrityVerificationStorage); + NON_MOVEABLE(IntegrityVerificationStorage); + public: + static constexpr s64 HashSize = crypto::Sha256Generator::HashSize; + + struct BlockHash { + u8 hash[HashSize]; + }; + static_assert(std::is_pod<BlockHash>::value); + private: + fs::SubStorage hash_storage; + fs::SubStorage data_storage; + s64 verification_block_size; + s64 verification_block_order; + s64 upper_layer_verification_block_size; + s64 upper_layer_verification_block_order; + IBufferManager *buffer_manager; + fs::HashSalt salt; + bool is_real_data; + fs::StorageType storage_type; + public: + IntegrityVerificationStorage() : verification_block_size(0), verification_block_order(0), upper_layer_verification_block_size(0), upper_layer_verification_block_order(0), buffer_manager(nullptr) { /* ... */ } + virtual ~IntegrityVerificationStorage() override { this->Finalize(); } + + Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type); + void Finalize(); + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result Write(s64 offset, const void *buffer, size_t size) override; + + virtual Result SetSize(s64 size) override { return fs::ResultUnsupportedOperationInIntegrityVerificationStorageA(); } + virtual Result GetSize(s64 *out) override; + + virtual Result Flush() override; + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + using IStorage::OperateRange; + + void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const; + + s64 GetBlockSize() const { + return this->verification_block_size; + } + private: + Result ReadBlockSignature(void *dst, size_t dst_size, s64 offset, size_t size); + Result WriteBlockSignature(const void *src, size_t src_size, s64 offset, size_t size); + Result VerifyHash(const void *buf, BlockHash *hash); + + void CalcBlockHash(BlockHash *out, const void *buffer) const { + return this->CalcBlockHash(out, buffer, static_cast<size_t>(this->verification_block_size)); + } + + Result IsCleared(bool *is_cleared, const BlockHash &hash); + private: + static void SetValidationBit(BlockHash *hash) { + AMS_ASSERT(hash != nullptr); + hash->hash[HashSize - 1] |= 0x80; + } + + static bool IsValidationBit(const BlockHash *hash) { + AMS_ASSERT(hash != nullptr); + return (hash->hash[HashSize - 1] & 0x80) != 0; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_save_types.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_save_types.hpp new file mode 100644 index 000000000..4657a4e60 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_save_types.hpp @@ -0,0 +1,41 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <stratosphere/lmem.hpp> +#include <stratosphere/fs/fs_directory.hpp> +#include <stratosphere/fs/fs_filesystem.hpp> +#include <stratosphere/fssystem/dbm/fssystem_dbm_utils.hpp> + +namespace ams::fssystem::save { + + constexpr inline bool IsPowerOfTwo(s32 val) { + return util::IsPowerOfTwo(val); + } + + constexpr inline u32 ILog2(u32 val) { + AMS_ASSERT(val > 0); + return (BITSIZEOF(u32) - 1 - dbm::CountLeadingZeros(val)); + } + + constexpr inline u32 CeilPowerOfTwo(u32 val) { + if (val == 0) { + return 1; + } + return ((1u << (BITSIZEOF(u32) - 1)) >> (dbm::CountLeadingZeros(val - 1) - 1)); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/hos/hos_stratosphere_api.hpp b/libraries/libstratosphere/include/stratosphere/hos/hos_stratosphere_api.hpp index 30e090e4c..6cc3147d5 100644 --- a/libraries/libstratosphere/include/stratosphere/hos/hos_stratosphere_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/hos/hos_stratosphere_api.hpp @@ -20,5 +20,6 @@ namespace ams::hos { void InitializeForStratosphere(); + void InitializeForStratosphereDebug(hos::Version debug_version); } diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp index 3b9639f82..506290b39 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_api.hpp @@ -15,19 +15,83 @@ */ #pragma once -#include "spl_types.hpp" +#include <stratosphere/spl/spl_types.hpp> namespace ams::spl { - HardwareType GetHardwareType(); - MemoryArrangement GetMemoryArrangement(); - bool IsDisabledProgramVerification(); - bool IsDevelopmentHardware(); - bool IsDevelopmentFunctionEnabled(); - bool IsMariko(); - bool IsRecoveryBoot(); + void Initialize(); + void InitializeForCrypto(); + void InitializeForSsl(); + void InitializeForEs(); + void InitializeForFs(); + void InitializeForManu(); - Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, u32 generation, u32 option); + void Finalize(); + + Result AllocateAesKeySlot(s32 *out_slot); + Result DeallocateAesKeySlot(s32 slot); + + Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, s32 generation, u32 option); + Result LoadAesKey(s32 slot, const AccessKey &access_key, const void *key_source, size_t key_source_size); Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size); + Result GenerateSpecificAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option); + Result ComputeCtr(void *dst, size_t dst_size, s32 slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + Result DecryptAesKey(void *dst, size_t dst_size, const void *src, size_t src_size, s32 generation, u32 option); + + Result GetConfig(u64 *out, ConfigItem item); + bool IsDevelopment(); + MemoryArrangement GetMemoryArrangement(); + + inline bool GetConfigBool(ConfigItem item) { + u64 v; + R_ABORT_UNLESS(::ams::spl::GetConfig(std::addressof(v), item)); + return v != 0; + } + + inline HardwareType GetHardwareType() { + u64 v; + R_ABORT_UNLESS(::ams::spl::GetConfig(std::addressof(v), ::ams::spl::ConfigItem::HardwareType)); + return static_cast<HardwareType>(v); + } + + inline HardwareState GetHardwareState() { + u64 v; + R_ABORT_UNLESS(::ams::spl::GetConfig(std::addressof(v), ::ams::spl::ConfigItem::HardwareState)); + return static_cast<HardwareState>(v); + } + + inline u64 GetDeviceIdLow() { + u64 v; + R_ABORT_UNLESS(::ams::spl::GetConfig(std::addressof(v), ::ams::spl::ConfigItem::DeviceId)); + return v; + } + + inline bool IsRecoveryBoot() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::IsRecoveryBoot); + } + + inline bool IsDevelopmentFunctionEnabled() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::IsDevelopmentFunctionEnabled); + } + + inline bool IsDisabledProgramVerification() { + return ::ams::spl::GetConfigBool(::ams::spl::ConfigItem::DisableProgramVerification); + } + + Result SetBootReason(BootReasonValue boot_reason); + Result GetBootReason(BootReasonValue *out); + + inline BootReasonValue GetBootReason() { + BootReasonValue br; + R_ABORT_UNLESS(::ams::spl::GetBootReason(std::addressof(br))); + return br; + } + + SocType GetSocType(); + + Result GetPackage2Hash(void *dst, size_t dst_size); + Result GenerateRandomBytes(void *out, size_t buffer_size); + + Result LoadPreparedAesKey(s32 slot, const AccessKey &access_key); } diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 43dbb54ed..b02bf1fd8 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -112,6 +112,17 @@ namespace ams::spl { Hoag = 2, Iowa = 3, Calcio = 4, + _Five_ = 5, + }; + + enum SocType { + SocType_Erista = 0, + SocType_Mariko = 1, + }; + + enum HardwareState { + HardwareState_Development = 0, + HardwareState_Production = 1, }; enum MemoryArrangement { @@ -185,23 +196,23 @@ namespace ams::spl { enum class ConfigItem : u32 { /* Standard config items. */ - DisableProgramVerification = 1, - DramId = 2, - SecurityEngineIrqNumber = 3, - Version = 4, - HardwareType = 5, - IsRetail = 6, - IsRecoveryBoot = 7, - DeviceId = 8, - BootReason = 9, - MemoryMode = 10, - IsDebugMode = 11, - KernelConfiguration = 12, - IsChargerHiZModeEnabled = 13, - IsQuest = 14, - RegulatorType = 15, - DeviceUniqueKeyGeneration = 16, - Package2Hash = 17, + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineIrqNumber = 3, + FuseVersion = 4, + HardwareType = 5, + HardwareState = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDevelopmentFunctionEnabled = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + IsQuest = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, + Package2Hash = 17, /* Extension config items for exosphere. */ ExosphereApiVersion = 65000, diff --git a/libraries/libstratosphere/source/fs/fs_file_storage.cpp b/libraries/libstratosphere/source/fs/fs_file_storage.cpp index f7ee4c14c..b56153fb0 100644 --- a/libraries/libstratosphere/source/fs/fs_file_storage.cpp +++ b/libraries/libstratosphere/source/fs/fs_file_storage.cpp @@ -90,6 +90,18 @@ namespace ams::fs { } } + Result FileStorageBasedFileSystem::Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const char *path, fs::OpenMode mode) { + /* Open the file. */ + std::unique_ptr<fs::fsa::IFile> base_file; + R_TRY(base_file_system->OpenFile(std::addressof(base_file), path, mode)); + + /* Set the file. */ + this->SetFile(std::move(base_file)); + this->base_file_system = std::move(base_file_system); + + return ResultSuccess(); + } + Result FileHandleStorage::UpdateSize() { R_SUCCEED_IF(this->size != InvalidSize); return GetFileSize(std::addressof(this->size), this->handle); diff --git a/libraries/libstratosphere/source/fssrv/fscreator/fssrv_partition_file_system_creator.cpp b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_partition_file_system_creator.cpp new file mode 100644 index 000000000..b64447ac8 --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_partition_file_system_creator.cpp @@ -0,0 +1,33 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv::fscreator { + + Result PartitionFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) { + /* Allocate a filesystem. */ + std::shared_ptr fs = fssystem::AllocateShared<fssystem::PartitionFileSystem>(); + R_UNLESS(fs != nullptr, fs::ResultAllocationFailureInPartitionFileSystemCreatorA()); + + /* Initialize the filesystem. */ + R_TRY(fs->Initialize(std::move(storage))); + + /* Set the output. */ + *out = std::move(fs); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fscreator/fssrv_rom_file_system_creator.cpp b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_rom_file_system_creator.cpp new file mode 100644 index 000000000..c17e7d24e --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_rom_file_system_creator.cpp @@ -0,0 +1,70 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv::fscreator { + + namespace { + + class RomFileSystemWithBuffer : public ::ams::fssystem::RomFsFileSystem { + private: + void *meta_cache_buffer; + size_t meta_cache_buffer_size; + MemoryResource *allocator; + public: + explicit RomFileSystemWithBuffer(MemoryResource *mr) : meta_cache_buffer(nullptr), allocator(mr) { /* ... */ } + + ~RomFileSystemWithBuffer() { + if (this->meta_cache_buffer != nullptr) { + this->allocator->Deallocate(this->meta_cache_buffer, this->meta_cache_buffer_size); + } + } + + Result Initialize(std::shared_ptr<fs::IStorage> storage) { + /* Check if the buffer is eligible for cache. */ + size_t buffer_size = 0; + if (R_FAILED(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(buffer_size), storage.get())) || buffer_size == 0 || buffer_size >= 128_KB) { + return RomFsFileSystem::Initialize(std::move(storage), nullptr, 0, false); + } + + /* Allocate a buffer. */ + this->meta_cache_buffer = this->allocator->Allocate(buffer_size); + if (this->meta_cache_buffer == nullptr) { + return RomFsFileSystem::Initialize(std::move(storage), nullptr, 0, false); + } + + /* Initialize with cache buffer. */ + this->meta_cache_buffer_size = buffer_size; + return RomFsFileSystem::Initialize(std::move(storage), this->meta_cache_buffer, this->meta_cache_buffer_size, true); + } + }; + + } + + Result RomFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::IStorage> storage) { + /* Allocate a filesystem. */ + std::shared_ptr fs = fssystem::AllocateShared<RomFileSystemWithBuffer>(this->allocator); + R_UNLESS(fs != nullptr, fs::ResultAllocationFailureInRomFileSystemCreatorA()); + + /* Initialize the filesystem. */ + R_TRY(fs->Initialize(std::move(storage))); + + /* Set the output. */ + *out = std::move(fs); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp new file mode 100644 index 000000000..cbaa0dfa8 --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp @@ -0,0 +1,166 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv::fscreator { + + Result StorageOnNcaCreator::VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) { + /* Open the npdm. */ + constexpr const char MetaFilePath[] = "/main.npdm"; + std::unique_ptr<fs::fsa::IFile> file; + R_TRY(fs->OpenFile(std::addressof(file), MetaFilePath, fs::OpenMode_Read)); + + size_t size; + + /* Read the Acid signature key generation. */ + constexpr s64 AcidSignatureKeyGenerationOffset = offsetof(ldr::Npdm, signature_key_generation); + u32 acid_signature_key_generation; + R_TRY(file->Read(std::addressof(size), AcidSignatureKeyGenerationOffset, std::addressof(acid_signature_key_generation), sizeof(acid_signature_key_generation), fs::ReadOption())); + R_UNLESS(size == sizeof(acid_signature_key_generation), fs::ResultInvalidAcidFileSize()); + + /* Read the Acid offset. */ + constexpr s64 AcidOffsetOffset = offsetof(ldr::Npdm, acid_offset); + s32 acid_offset; + R_TRY(file->Read(std::addressof(size), AcidOffsetOffset, std::addressof(acid_offset), sizeof(acid_offset), fs::ReadOption())); + R_UNLESS(size == sizeof(acid_offset), fs::ResultInvalidAcidFileSize()); + + /* Read the Acid size. */ + constexpr s64 AcidSizeOffset = offsetof(ldr::Npdm, acid_size); + s32 acid_size; + R_TRY(file->Read(std::addressof(size), AcidSizeOffset, std::addressof(acid_size), sizeof(acid_size), fs::ReadOption())); + R_UNLESS(size == sizeof(acid_size), fs::ResultInvalidAcidFileSize()); + + /* Allocate memory for the acid. */ + u8 *acid = static_cast<u8 *>(this->allocator->Allocate(acid_size)); + R_UNLESS(acid != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorA()); + ON_SCOPE_EXIT { this->allocator->Deallocate(acid, acid_size); }; + + /* Read the acid. */ + R_TRY(file->Read(std::addressof(size), acid_offset, acid, acid_size, fs::ReadOption())); + R_UNLESS(size == static_cast<size_t>(acid_size), fs::ResultInvalidAcidSize()); + + /* Define interesting extents. */ + constexpr s32 AcidSignOffset = 0x000; + constexpr s32 AcidSignSize = 0x100; + constexpr s32 HeaderSign2KeyOffset = 0x100; + constexpr s32 HeaderSign2KeySize = 0x100; + constexpr s32 AcidSignTargetOffset = 0x100; + constexpr s32 AcidSignTargetSizeOffset = 0x204; + + /* Read the sign target size. */ + R_UNLESS(acid_size >= static_cast<s32>(AcidSignTargetSizeOffset + sizeof(s32)), fs::ResultInvalidAcidSize()); + const s32 acid_sign_target_size = *reinterpret_cast<const s32 *>(acid + AcidSignTargetSizeOffset); + + /* Validate the sign target size. */ + R_UNLESS(acid_size >= static_cast<s32>(acid_sign_target_size + sizeof(s32)), fs::ResultInvalidAcidSize()); + R_UNLESS(acid_size >= AcidSignTargetOffset + acid_sign_target_size, fs::ResultInvalidAcidSize()); + + /* Verify the signature. */ + { + const u8 *sig = acid + AcidSignOffset; + const size_t sig_size = static_cast<size_t>(AcidSignSize); + const u8 *mod = fssystem::GetAcidSignatureKeyModulus(this->is_prod, acid_signature_key_generation); + const size_t mod_size = fssystem::AcidSignatureKeyModulusSize; + const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent(); + const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize; + const u8 *msg = acid + AcidSignTargetOffset; + const size_t msg_size = acid_sign_target_size; + const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); + if (!is_signature_valid) { + /* If the signature is invalid, then unless program verification is disabled error out. */ + R_UNLESS(!this->is_enabled_program_verification, fs::ResultAcidVerificationFailed()); + + /* If program verification is disabled, then we're fine. */ + return ResultSuccess(); + } + } + + /* If we have an nca reader, verify the header signature using the key from the acid. */ + if (nca_reader) { + /* Verify that the acid contains a key to validate the second signature with. */ + R_UNLESS(acid_size >= HeaderSign2KeyOffset + HeaderSign2KeySize, fs::ResultInvalidAcidSize()); + + /* Validate that this key has its top byte set (and is thus approximately 2048 bits). */ + R_UNLESS(*(acid + HeaderSign2KeyOffset + HeaderSign2KeySize - 1) != 0x00, fs::ResultInvalidAcid()); + + R_TRY(nca_reader->VerifyHeaderSign2(reinterpret_cast<char *>(acid) + HeaderSign2KeyOffset, HeaderSign2KeySize)); + } + + return ResultSuccess(); + } + + Result StorageOnNcaCreator::Create(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) { + /* Create a fs driver. */ + fssystem::NcaFileSystemDriver nca_fs_driver(nca_reader, this->allocator, this->buffer_manager); + + /* Open the storage. */ + std::shared_ptr<fs::IStorage> storage; + R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index)); + + /* If we should, verify the header signature. */ + if (verify_header_sign_2) { + R_TRY(this->VerifyNcaHeaderSign2(nca_reader.get(), storage.get())); + } + + /* Set the out storage. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result StorageOnNcaCreator::CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) { + /* Create a fs driver. */ + fssystem::NcaFileSystemDriver nca_fs_driver(original_nca_reader, current_nca_reader, this->allocator, this->buffer_manager); + + /* Open the storage. */ + std::shared_ptr<fs::IStorage> storage; + R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index)); + + /* If we should, verify the header signature. */ + if (verify_header_sign_2) { + R_TRY(this->VerifyNcaHeaderSign2(current_nca_reader.get(), storage.get())); + } + + /* Set the out storage. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result StorageOnNcaCreator::CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) { + /* Create a reader. */ + std::shared_ptr reader = fssystem::AllocateShared<fssystem::NcaReader>(); + R_UNLESS(reader != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorB()); + + /* Initialize the reader. */ + R_TRY(reader->Initialize(std::move(storage), this->nca_crypto_cfg)); + + /* Set the output. */ + *out = std::move(reader); + return ResultSuccess(); + } + + void StorageOnNcaCreator::SetEnabledProgramVerification(bool en) { + if (!this->is_prod) { + this->is_enabled_program_verification = en; + } + } + + Result StorageOnNcaCreator::VerifyNcaHeaderSign2(fssystem::NcaReader *nca_reader, fs::IStorage *storage) { + fssystem::PartitionFileSystem part_fs; + R_TRY(part_fs.Initialize(storage)); + return this->VerifyAcid(std::addressof(part_fs), nca_reader); + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fssrv_file_system_proxy_api.cpp b/libraries/libstratosphere/source/fssrv/fssrv_file_system_proxy_api.cpp new file mode 100644 index 000000000..7861a2717 --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fssrv_file_system_proxy_api.cpp @@ -0,0 +1,24 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv { + + void InitializeForFileSystemProxy(fscreator::FileSystemCreatorInterfaces *fs_creator_interfaces, fssystem::IBufferManager *buffer_manager, bool is_development_function_enabled) { + /* TODO FS-REIMPL */ + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_exp_heap.cpp b/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_exp_heap.cpp new file mode 100644 index 000000000..fcb611df9 --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_exp_heap.cpp @@ -0,0 +1,57 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv { + + namespace { + + size_t GetUsedSize(void *p) { + const auto block_head = reinterpret_cast<const lmem::impl::ExpHeapMemoryBlockHead *>(reinterpret_cast<uintptr_t>(p) - sizeof(lmem::impl::ExpHeapMemoryBlockHead)); + return block_head->block_size + ((block_head->attributes >> 8) & 0x7F) + sizeof(lmem::impl::ExpHeapMemoryBlockHead); + } + + } + + void PeakCheckableMemoryResourceFromExpHeap::OnAllocate(void *p, size_t size) { + if (p != nullptr) { + this->current_free_size = GetUsedSize(p); + this->peak_free_size = std::min(this->peak_free_size, this->current_free_size); + } + } + + void PeakCheckableMemoryResourceFromExpHeap::OnDeallocate(void *p, size_t size) { + if (p != nullptr) { + this->current_free_size += GetUsedSize(p); + } + } + + void *PeakCheckableMemoryResourceFromExpHeap::AllocateImpl(size_t size, size_t align) { + std::scoped_lock lk(this->mutex); + + void *p = lmem::AllocateFromExpHeap(this->heap_handle, size, static_cast<s32>(align)); + this->OnAllocate(p, size); + return p; + } + + void PeakCheckableMemoryResourceFromExpHeap::DeallocateImpl(void *p, size_t size, size_t align) { + std::scoped_lock lk(this->mutex); + + this->OnDeallocate(p, size); + lmem::FreeToExpHeap(this->heap_handle, p); + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_standard_allocator.cpp b/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_standard_allocator.cpp new file mode 100644 index 000000000..5270331fa --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fssrv_memory_resource_from_standard_allocator.cpp @@ -0,0 +1,53 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv { + + MemoryResourceFromStandardAllocator::MemoryResourceFromStandardAllocator(mem::StandardAllocator *allocator) : allocator(allocator), mutex() { + this->current_free_size = this->allocator->GetTotalFreeSize(); + this->ClearPeak(); + } + + void MemoryResourceFromStandardAllocator::ClearPeak() { + std::scoped_lock lk(this->mutex); + this->peak_free_size = this->current_free_size; + this->peak_allocated_size = 0; + } + + void *MemoryResourceFromStandardAllocator::AllocateImpl(size_t size, size_t align) { + std::scoped_lock lk(this->mutex); + + void *p = this->allocator->Allocate(size, align); + + if (p != nullptr) { + this->current_free_size -= this->allocator->GetSizeOf(p); + this->peak_free_size = std::min(this->peak_free_size, this->current_free_size); + } + + this->peak_allocated_size = std::max(this->peak_allocated_size, size); + + return p; + } + + void MemoryResourceFromStandardAllocator::DeallocateImpl(void *p, size_t size, size_t align) { + std::scoped_lock lk(this->mutex); + + this->current_free_size += this->allocator->GetSizeOf(p); + this->allocator->Free(p); + } + +} diff --git a/libraries/libstratosphere/source/fssrv/fssrv_nca_crypto_configuration.cpp b/libraries/libstratosphere/source/fssrv/fssrv_nca_crypto_configuration.cpp new file mode 100644 index 000000000..0597a6156 --- /dev/null +++ b/libraries/libstratosphere/source/fssrv/fssrv_nca_crypto_configuration.cpp @@ -0,0 +1,188 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssrv { + + namespace { + + constexpr inline const u8 HeaderSign1KeyModulusDev[fssystem::NcaCryptoConfiguration::Header1SignatureKeyGenerationMax + 1][fssystem::NcaCryptoConfiguration::Rsa2048KeyModulusSize] = { + { + 0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4, + 0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49, + 0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C, + 0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B, + 0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4, + 0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47, + 0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE, + 0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D, + 0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E, + 0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0, + 0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D, + 0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E, + 0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B, + 0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E, + 0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62, + 0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9 + }, + { + 0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, 0x38, 0x5E, 0xD1, 0x01, + 0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66, + 0xD3, 0x99, 0x13, 0x8A, 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96, + 0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, 0x67, 0x3A, 0x29, 0x36, + 0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F, + 0xD3, 0x5B, 0x01, 0x1B, 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38, + 0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, 0xEC, 0x1D, 0x41, 0x69, + 0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74, + 0xF8, 0xEC, 0xEA, 0x00, 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32, + 0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, 0xD2, 0x86, 0x14, 0x3C, + 0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6, + 0x54, 0x82, 0x90, 0x36, 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA, + 0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, 0x08, 0xC5, 0x20, 0x1E, + 0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01, + 0xAC, 0x60, 0xE0, 0xED, 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9, + 0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, 0xE9, 0x91, 0x4C, 0x49 + } + }; + + constexpr inline const u8 HeaderSign1KeyModulusProd[fssystem::NcaCryptoConfiguration::Header1SignatureKeyGenerationMax + 1][fssystem::NcaCryptoConfiguration::Rsa2048KeyModulusSize] = { + { + 0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F, + 0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58, + 0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16, + 0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2, + 0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF, + 0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67, + 0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26, + 0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C, + 0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE, + 0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86, + 0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35, + 0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B, + 0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29, + 0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40, + 0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81, + 0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03 + }, + { + 0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6, + 0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48, + 0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24, + 0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16, + 0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6, + 0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2, + 0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94, + 0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B, + 0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37, + 0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4, + 0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76, + 0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9, + 0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2, + 0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B, + 0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA, + 0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD + } + }; + + constexpr inline const ::ams::fssystem::NcaCryptoConfiguration DefaultNcaCryptoConfigurationDev = { + /* Header1 Signature Key Moduli */ + { HeaderSign1KeyModulusDev[0], HeaderSign1KeyModulusDev[1] }, + + /* Header 1 Signature Key Public Exponent */ + { 0x01, 0x00, 0x01 }, + + /* Key Area Encryption Key Sources */ + { + /* Application */ + { 0x7F, 0x59, 0x97, 0x1E, 0x62, 0x9F, 0x36, 0xA1, 0x30, 0x98, 0x06, 0x6F, 0x21, 0x44, 0xC3, 0x0D }, + + /* Ocean */ + { 0x32, 0x7D, 0x36, 0x08, 0x5A, 0xD1, 0x75, 0x8D, 0xAB, 0x4E, 0x6F, 0xBA, 0xA5, 0x55, 0xD8, 0x82 }, + + /* System */ + { 0x87, 0x45, 0xF1, 0xBB, 0xA6, 0xBE, 0x79, 0x64, 0x7D, 0x04, 0x8B, 0xA6, 0x7B, 0x5F, 0xDA, 0x4A }, + }, + + /* Header Encryption Key Source */ + { 0x1F, 0x12, 0x91, 0x3A, 0x4A, 0xCB, 0xF0, 0x0D, 0x4C, 0xDE, 0x3A, 0xF6, 0xD5, 0x23, 0x88, 0x2A }, + + /* Encrypted Header Encryption Key */ + { + { 0x5A, 0x3E, 0xD8, 0x4F, 0xDE, 0xC0, 0xD8, 0x26, 0x31, 0xF7, 0xE2, 0x5D, 0x19, 0x7B, 0xF5, 0xD0 }, + { 0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2 } + }, + + /* Key Generation Function */ + nullptr, + + /* Decrypt Aes Ctr Function */ + nullptr, + + /* Decrypt Aes Ctr External Function */ + nullptr, + + /* Plaintext Header Available */ + false, + }; + + constexpr inline const ::ams::fssystem::NcaCryptoConfiguration DefaultNcaCryptoConfigurationProd = { + /* Header1 Signature Key Moduli */ + { HeaderSign1KeyModulusProd[0], HeaderSign1KeyModulusProd[1] }, + + /* Header 1 Signature Key Public Exponent */ + { 0x01, 0x00, 0x01 }, + + /* Key Area Encryption Key Sources */ + { + /* Application */ + { 0x7F, 0x59, 0x97, 0x1E, 0x62, 0x9F, 0x36, 0xA1, 0x30, 0x98, 0x06, 0x6F, 0x21, 0x44, 0xC3, 0x0D }, + + /* Ocean */ + { 0x32, 0x7D, 0x36, 0x08, 0x5A, 0xD1, 0x75, 0x8D, 0xAB, 0x4E, 0x6F, 0xBA, 0xA5, 0x55, 0xD8, 0x82 }, + + /* System */ + { 0x87, 0x45, 0xF1, 0xBB, 0xA6, 0xBE, 0x79, 0x64, 0x7D, 0x04, 0x8B, 0xA6, 0x7B, 0x5F, 0xDA, 0x4A }, + }, + + /* Header Encryption Key Source */ + { 0x1F, 0x12, 0x91, 0x3A, 0x4A, 0xCB, 0xF0, 0x0D, 0x4C, 0xDE, 0x3A, 0xF6, 0xD5, 0x23, 0x88, 0x2A }, + + /* Encrypted Header Encryption Key */ + { + { 0x5A, 0x3E, 0xD8, 0x4F, 0xDE, 0xC0, 0xD8, 0x26, 0x31, 0xF7, 0xE2, 0x5D, 0x19, 0x7B, 0xF5, 0xD0 }, + { 0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2 } + }, + + /* Key Generation Function */ + nullptr, + + /* Decrypt Aes Ctr Function */ + nullptr, + + /* Decrypt Aes Ctr External Function */ + nullptr, + + /* Plaintext Header Available */ + false, + }; + + } + + const ::ams::fssystem::NcaCryptoConfiguration *GetDefaultNcaCryptoConfiguration(bool prod) { + return prod ? std::addressof(DefaultNcaCryptoConfigurationProd) : std::addressof(DefaultNcaCryptoConfigurationDev); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp b/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp new file mode 100644 index 000000000..34bdb7b11 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/buffers/fssystem_file_system_buffer_manager.cpp @@ -0,0 +1,356 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + Result FileSystemBufferManager::CacheHandleTable::Initialize(s32 max_cache_count) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries == nullptr); + AMS_ASSERT(this->internal_entry_buffer == nullptr); + + /* If we don't have an external buffer, try to allocate an internal one. */ + if (this->external_entry_buffer == nullptr) { + this->entry_buffer_size = sizeof(Entry) * max_cache_count; + this->internal_entry_buffer = fs::impl::MakeUnique<char[]>(this->entry_buffer_size); + } + + /* We need to have at least one entry buffer. */ + R_UNLESS(this->internal_entry_buffer != nullptr || this->external_entry_buffer != nullptr, fs::ResultAllocationFailureInFileSystemBufferManagerA()); + + /* Set entries. */ + this->entries = reinterpret_cast<Entry *>(this->external_entry_buffer != nullptr ? this->external_entry_buffer : this->internal_entry_buffer.get()); + this->entry_count = 0; + this->entry_count_max = max_cache_count; + AMS_ASSERT(this->entries != nullptr); + + this->cache_count_min = max_cache_count / 16; + this->cache_size_min = this->cache_count_min * 0x100; + + return ResultSuccess(); + } + + void FileSystemBufferManager::CacheHandleTable::Finalize() { + if (this->entries != nullptr) { + AMS_ASSERT(this->entry_count == 0); + + if (this->external_attr_info_buffer == nullptr) { + auto it = this->attr_list.begin(); + while (it != this->attr_list.end()) { + const auto attr_info = std::addressof(*it); + it = this->attr_list.erase(it); + delete attr_info; + } + } + + this->internal_entry_buffer.reset(); + this->external_entry_buffer = nullptr; + this->entry_buffer_size = 0; + this->entries = nullptr; + this->total_cache_size = 0; + } + } + + bool FileSystemBufferManager::CacheHandleTable::Register(CacheHandle *out, uintptr_t address, size_t size, const BufferAttribute &attr) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + AMS_ASSERT(out != nullptr); + + /* Get the entry. */ + auto entry = this->AcquireEntry(address, size, attr); + + /* If we don't have an entry, we can't register. */ + if (entry == nullptr) { + return false; + } + + /* Get the attr info. If we have one, increment. */ + if (const auto attr_info = this->FindAttrInfo(attr); attr_info != nullptr) { + attr_info->IncrementCacheCount(); + attr_info->AddCacheSize(size); + } else { + /* Make a new attr info and add it to the list. */ + AttrInfo *new_info = nullptr; + + if (this->external_attr_info_buffer == nullptr) { + new_info = new AttrInfo(attr.GetLevel(), 1, size); + } else if (0 <= attr.GetLevel() && attr.GetLevel() < this->external_attr_info_count) { + const auto buffer = this->external_attr_info_buffer + attr.GetLevel() * sizeof(AttrInfo); + new_info = new (buffer) AttrInfo(attr.GetLevel(), 1, size); + } + + /* If we failed to make a new attr info, we can't register. */ + if (new_info == nullptr) { + this->ReleaseEntry(entry); + return false; + } + + this->attr_list.push_back(*new_info); + } + + this->total_cache_size += size; + *out = entry->GetHandle(); + return true; + } + + bool FileSystemBufferManager::CacheHandleTable::Unregister(uintptr_t *out_address, size_t *out_size, CacheHandle handle) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + AMS_ASSERT(out_address != nullptr); + AMS_ASSERT(out_size != nullptr); + + /* Find the lower bound for the entry. */ + const auto entry = std::lower_bound(this->entries, this->entries + this->entry_count, handle, [](const Entry &entry, CacheHandle handle) { + return entry.GetHandle() < handle; + }); + + /* If the entry is a match, unregister it. */ + if (entry != this->entries + this->entry_count && entry->GetHandle() == handle) { + this->UnregisterCore(out_address, out_size, entry); + return true; + } else { + return false; + } + } + + bool FileSystemBufferManager::CacheHandleTable::UnregisterOldest(uintptr_t *out_address, size_t *out_size, const BufferAttribute &attr, size_t required_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + AMS_ASSERT(out_address != nullptr); + AMS_ASSERT(out_size != nullptr); + + /* If we have no entries, we can't unregister any. */ + if (this->entry_count == 0) { + return false; + } + + const auto CanUnregister = [this](const Entry &entry) { + const auto attr_info = this->FindAttrInfo(entry.GetBufferAttribute()); + AMS_ASSERT(attr_info != nullptr); + + const auto ccm = this->GetCacheCountMin(entry.GetBufferAttribute()); + const auto csm = this->GetCacheSizeMin(entry.GetBufferAttribute()); + + return ccm < attr_info->GetCacheCount() && csm + entry.GetSize() <= attr_info->GetCacheSize(); + }; + + /* Find an entry, falling back to the first entry. */ + auto entry = std::find_if(this->entries, this->entries + this->entry_count, CanUnregister); + if (entry == this->entries + this->entry_count) { + entry = this->entries; + } + + AMS_ASSERT(entry != this->entries + this->entry_count); + this->UnregisterCore(out_address, out_size, entry); + return true; + } + + void FileSystemBufferManager::CacheHandleTable::UnregisterCore(uintptr_t *out_address, size_t *out_size, Entry *entry) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + AMS_ASSERT(out_address != nullptr); + AMS_ASSERT(out_size != nullptr); + AMS_ASSERT(entry != nullptr); + + /* Get the attribute info. */ + const auto attr_info = this->FindAttrInfo(entry->GetBufferAttribute()); + AMS_ASSERT(attr_info != nullptr); + AMS_ASSERT(attr_info->GetCacheCount() > 0); + AMS_ASSERT(attr_info->GetCacheSize() >= entry->GetSize()); + + /* Release from the attr info. */ + attr_info->DecrementCacheCount(); + attr_info->SubtractCacheSize(entry->GetSize()); + + /* Release from cached size. */ + AMS_ASSERT(this->total_cache_size >= entry->GetSize()); + this->total_cache_size -= entry->GetSize(); + + /* Release the entry. */ + *out_address = entry->GetAddress(); + *out_size = entry->GetSize(); + this->ReleaseEntry(entry); + } + + FileSystemBufferManager::CacheHandle FileSystemBufferManager::CacheHandleTable::PublishCacheHandle() { + AMS_ASSERT(this->entries != nullptr); + return (++this->current_handle); + } + + size_t FileSystemBufferManager::CacheHandleTable::GetTotalCacheSize() const { + return this->total_cache_size; + } + + FileSystemBufferManager::CacheHandleTable::Entry *FileSystemBufferManager::CacheHandleTable::AcquireEntry(uintptr_t address, size_t size, const BufferAttribute &attr) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + + Entry *entry = nullptr; + if (this->entry_count < this->entry_count_max) { + entry = this->entries + this->entry_count; + entry->Initialize(this->PublishCacheHandle(), address, size, attr); + ++this->entry_count; + AMS_ASSERT(this->entry_count == 1 || (entry-1)->GetHandle() < entry->GetHandle()); + } + + return entry; + } + + void FileSystemBufferManager::CacheHandleTable::ReleaseEntry(Entry *entry) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->entries != nullptr); + AMS_ASSERT(entry != nullptr); + + /* Ensure the entry is valid. */ + const auto entry_buffer = this->external_entry_buffer != nullptr ? this->external_entry_buffer : this->internal_entry_buffer.get(); + AMS_ASSERT(static_cast<void *>(entry_buffer) <= static_cast<void *>(entry)); + AMS_ASSERT(static_cast<void *>(entry) < static_cast<void *>(entry_buffer + this->entry_buffer_size)); + + /* Copy the entries back by one. */ + std::memmove(entry, entry + 1, sizeof(Entry) * (this->entry_count - ((entry + 1) - this->entries))); + + /* Decrement our entry count. */ + --this->entry_count; + } + + FileSystemBufferManager::CacheHandleTable::AttrInfo *FileSystemBufferManager::CacheHandleTable::FindAttrInfo(const BufferAttribute &attr) { + const auto it = std::find_if(this->attr_list.begin(), this->attr_list.end(), [&attr](const AttrInfo &info) { + return attr.GetLevel() == attr.GetLevel(); + }); + + return it != this->attr_list.end() ? std::addressof(*it) : nullptr; + } + + const std::pair<uintptr_t, size_t> FileSystemBufferManager::AllocateBufferImpl(size_t size, const BufferAttribute &attr) { + std::scoped_lock lk(this->mutex); + + std::pair<uintptr_t, size_t> range = {}; + const auto order = this->buddy_heap.GetOrderFromBytes(size); + AMS_ASSERT(order >= 0); + + while (true) { + if (auto address = this->buddy_heap.AllocateByOrder(order); address != 0) { + const auto allocated_size = this->buddy_heap.GetBytesFromOrder(order); + AMS_ASSERT(size <= allocated_size); + + range.first = reinterpret_cast<uintptr_t>(address); + range.second = allocated_size; + + const size_t free_size = this->buddy_heap.GetTotalFreeSize(); + this->peak_free_size = std::min(this->peak_free_size, free_size); + + const size_t total_allocatable_size = free_size + this->cache_handle_table.GetTotalCacheSize(); + this->peak_total_allocatable_size = std::min(this->peak_total_allocatable_size, total_allocatable_size); + break; + } + + /* Deallocate a buffer. */ + uintptr_t deallocate_address = 0; + size_t deallocate_size = 0; + + ++this->retried_count; + if (this->cache_handle_table.UnregisterOldest(std::addressof(deallocate_address), std::addressof(deallocate_size), attr, size)) { + this->DeallocateBuffer(deallocate_address, deallocate_size); + } else { + break; + } + } + + return range; + } + + void FileSystemBufferManager::DeallocateBufferImpl(uintptr_t address, size_t size) { + AMS_ASSERT(util::IsPowerOfTwo(size)); + + std::scoped_lock lk(this->mutex); + + this->buddy_heap.Free(reinterpret_cast<void *>(address), this->buddy_heap.GetOrderFromBytes(size)); + } + + FileSystemBufferManager::CacheHandle FileSystemBufferManager::RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) { + std::scoped_lock lk(this->mutex); + + CacheHandle handle = 0; + while (true) { + /* Try to register the handle. */ + if (this->cache_handle_table.Register(std::addressof(handle), address, size, attr)) { + break; + } + + /* Deallocate a buffer. */ + uintptr_t deallocate_address = 0; + size_t deallocate_size = 0; + + ++this->retried_count; + if (this->cache_handle_table.UnregisterOldest(std::addressof(deallocate_address), std::addressof(deallocate_size), attr)) { + this->DeallocateBuffer(deallocate_address, deallocate_size); + } else { + this->DeallocateBuffer(address, size); + handle = this->cache_handle_table.PublishCacheHandle(); + break; + } + } + + return handle; + } + + const std::pair<uintptr_t, size_t> FileSystemBufferManager::AcquireCacheImpl(CacheHandle handle) { + std::scoped_lock lk(this->mutex); + + std::pair<uintptr_t, size_t> range = {}; + if (this->cache_handle_table.Unregister(std::addressof(range.first), std::addressof(range.second), handle)) { + const size_t total_allocatable_size = this->buddy_heap.GetTotalFreeSize() + this->cache_handle_table.GetTotalCacheSize(); + this->peak_total_allocatable_size = std::min(this->peak_total_allocatable_size, total_allocatable_size); + } else { + range.first = 0; + range.second = 0; + } + + return range; + } + + size_t FileSystemBufferManager::GetTotalSizeImpl() const { + return this->total_size; + } + + size_t FileSystemBufferManager::GetFreeSizeImpl() const { + std::scoped_lock lk(this->mutex); + + return this->buddy_heap.GetTotalFreeSize(); + } + + size_t FileSystemBufferManager::GetTotalAllocatableSizeImpl() const { + return this->GetFreeSize() + this->cache_handle_table.GetTotalCacheSize(); + } + + size_t FileSystemBufferManager::GetPeakFreeSizeImpl() const { + return this->peak_free_size; + } + + size_t FileSystemBufferManager::GetPeakTotalAllocatableSizeImpl() const { + return this->peak_total_allocatable_size; + } + + size_t FileSystemBufferManager::GetRetriedCountImpl() const { + return this->retried_count; + } + + void FileSystemBufferManager::ClearPeakImpl() { + this->peak_free_size = this->GetFreeSize(); + this->retried_count = 0; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp new file mode 100644 index 000000000..a0177d1c8 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp @@ -0,0 +1,296 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + class SoftwareDecryptor final : public AesCtrCounterExtendedStorage::IDecryptor { + public: + virtual void Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) override final; + virtual bool HasExternalDecryptionKey() const override final { return false; } + }; + + class ExternalDecryptor final : public AesCtrCounterExtendedStorage::IDecryptor { + public: + static constexpr size_t BlockSize = AesCtrCounterExtendedStorage::BlockSize; + static constexpr size_t KeySize = AesCtrCounterExtendedStorage::KeySize; + static constexpr size_t IvSize = AesCtrCounterExtendedStorage::IvSize; + private: + AesCtrCounterExtendedStorage::DecryptFunction decrypt_function; + s32 key_index; + public: + ExternalDecryptor(AesCtrCounterExtendedStorage::DecryptFunction df, s32 key_idx) : decrypt_function(df), key_index(key_idx) { + AMS_ASSERT(this->decrypt_function != nullptr); + } + public: + virtual void Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) override final; + virtual bool HasExternalDecryptionKey() const override final { return this->key_index < 0; } + }; + + } + + Result AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index) { + std::unique_ptr<IDecryptor> decryptor = std::make_unique<ExternalDecryptor>(func, key_index); + R_UNLESS(decryptor != nullptr, fs::ResultAllocationFailureInAesCtrCounterExtendedStorageA()); + *out = std::move(decryptor); + return ResultSuccess(); + } + + Result AesCtrCounterExtendedStorage::CreateSoftwareDecryptor(std::unique_ptr<IDecryptor> *out) { + std::unique_ptr<IDecryptor> decryptor = std::make_unique<SoftwareDecryptor>(); + R_UNLESS(decryptor != nullptr, fs::ResultAllocationFailureInAesCtrCounterExtendedStorageA()); + *out = std::move(decryptor); + return ResultSuccess(); + } + + Result AesCtrCounterExtendedStorage::Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage) { + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + R_TRY(table_storage.Read(0, std::addressof(header), sizeof(header))); + R_TRY(header.Verify()); + + /* Determine extents. */ + const auto node_storage_size = QueryNodeStorageSize(header.entry_count); + const auto entry_storage_size = QueryEntryStorageSize(header.entry_count); + const auto node_storage_offset = QueryHeaderStorageSize(); + const auto entry_storage_offset = node_storage_offset + node_storage_size; + + /* Create a software decryptor. */ + std::unique_ptr<IDecryptor> sw_decryptor; + R_TRY(CreateSoftwareDecryptor(std::addressof(sw_decryptor))); + + /* Initialize. */ + return this->Initialize(allocator, key, key_size, secure_value, 0, data_storage, fs::SubStorage(std::addressof(table_storage), node_storage_offset, node_storage_size), fs::SubStorage(std::addressof(table_storage), entry_storage_offset, entry_storage_size), header.entry_count, std::move(sw_decryptor)); + } + + Result AesCtrCounterExtendedStorage::Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, s64 counter_offset, fs::SubStorage data_storage, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count, std::unique_ptr<IDecryptor> &&decryptor) { + /* Validate preconditions. */ + AMS_ASSERT(key != nullptr); + AMS_ASSERT(key_size == KeySize); + AMS_ASSERT(counter_offset >= 0); + AMS_ASSERT(decryptor != nullptr); + + /* Initialize the bucket tree table. */ + R_TRY(this->table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count)); + + /* Set members. */ + this->data_storage = data_storage; + std::memcpy(this->key, key, key_size); + this->secure_value = secure_value; + this->counter_offset = counter_offset; + this->decryptor = std::move(decryptor); + + return ResultSuccess(); + } + + void AesCtrCounterExtendedStorage::Finalize() { + if (this->IsInitialized()) { + this->table.Finalize(); + this->data_storage = fs::SubStorage(); + } + } + + Result AesCtrCounterExtendedStorage::Read(s64 offset, void *buffer, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Allow zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset()); + R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize()); + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + + /* Read the data. */ + R_TRY(this->data_storage.Read(offset, buffer, size)); + + /* Temporarily increase our thread priority. */ + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + + /* Find the offset in our tree. */ + BucketTree::Visitor visitor; + R_TRY(this->table.Find(std::addressof(visitor), offset)); + { + const auto entry_offset = visitor.Get<Entry>()->GetOffset(); + R_UNLESS(util::IsAligned(entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + R_UNLESS(0 <= entry_offset && this->table.Includes(entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + } + + /* Prepare to read in chunks. */ + u8 *cur_data = static_cast<u8 *>(buffer); + auto cur_offset = offset; + const auto end_offset = offset + static_cast<s64>(size); + + while (cur_offset < end_offset) { + /* Get the current entry. */ + const auto cur_entry = *visitor.Get<Entry>(); + + /* Get and validate the entry's offset. */ + const auto cur_entry_offset = cur_entry.GetOffset(); + R_UNLESS(cur_entry_offset <= cur_offset, fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + + /* Get and validate the next entry offset. */ + s64 next_entry_offset; + if (visitor.CanMoveNext()) { + R_TRY(visitor.MoveNext()); + next_entry_offset = visitor.Get<Entry>()->GetOffset(); + R_UNLESS(this->table.Includes(next_entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + } else { + next_entry_offset = this->table.GetEnd(); + } + R_UNLESS(util::IsAligned(next_entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); + + /* Get the offset of the entry in the data we read. */ + const auto data_offset = cur_offset - cur_entry_offset; + const auto data_size = (next_entry_offset - cur_entry_offset) - data_offset; + AMS_ASSERT(data_size > 0); + + /* Determine how much is left. */ + const auto remaining_size = end_offset - cur_offset; + const auto cur_size = static_cast<size_t>(std::min(remaining_size, data_size)); + AMS_ASSERT(cur_size <= size); + + /* Make the CTR for the data we're decrypting. */ + const auto counter_offset = this->counter_offset + cur_entry_offset + data_offset; + NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast<u32>(cur_entry.generation), .secure_value = this->secure_value } }; + + u8 iv[IvSize]; + AesCtrStorage::MakeIv(iv, IvSize, upper_iv.value, counter_offset); + + /* Decrypt. */ + this->decryptor->Decrypt(cur_data, cur_size, this->key, KeySize, iv, IvSize); + + /* Advance. */ + cur_data += cur_size; + cur_offset += cur_size; + } + + return ResultSuccess(); + } + + Result AesCtrCounterExtendedStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + switch (op_id) { + case fs::OperationId::InvalidateCache: + { + /* Validate preconditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Succeed if there's nothing to operate on. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset()); + R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize()); + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + + /* Invalidate our table's cache. */ + R_TRY(this->table.InvalidateCache()); + + /* Operate on our data storage. */ + R_TRY(this->data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + + return ResultSuccess(); + } + case fs::OperationId::QueryRange: + { + /* Validate preconditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Validate that we have an output range info. */ + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize()); + + /* Succeed if there's nothing to operate on. */ + if (size == 0) { + reinterpret_cast<fs::QueryRangeInfo *>(dst)->Clear(); + return ResultSuccess(); + } + + /* Validate arguments. */ + R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset()); + R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize()); + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + + /* Operate on our data storage. */ + R_TRY(this->data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + + /* Add in new flags. */ + fs::QueryRangeInfo new_info; + new_info.Clear(); + new_info.aes_ctr_key_type = static_cast<s32>(this->decryptor->HasExternalDecryptionKey() ? fs::AesCtrKeyTypeFlag::ExternalKeyForHardwareAes : fs::AesCtrKeyTypeFlag::InternalKeyForHardwareAes); + + /* Merge in the new info. */ + reinterpret_cast<fs::QueryRangeInfo *>(dst)->Merge(new_info); + + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageC(); + } + } + + void SoftwareDecryptor::Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) { + crypto::DecryptAes128Ctr(buf, buf_size, enc_key, enc_key_size, iv, iv_size, buf, buf_size); + } + + void ExternalDecryptor::Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) { + /* Validate preconditions. */ + AMS_ASSERT(buf != nullptr); + AMS_ASSERT(enc_key != nullptr); + AMS_ASSERT(enc_key_size == KeySize); + AMS_ASSERT(iv != nullptr); + AMS_ASSERT(iv_size == IvSize); + + /* Copy the ctr. */ + u8 ctr[IvSize]; + std::memcpy(ctr, iv, IvSize); + + /* Setup tracking. */ + size_t remaining_size = buf_size; + s64 cur_offset = 0; + + /* Allocate a pooled buffer for decryption. */ + PooledBuffer pooled_buffer; + pooled_buffer.AllocateParticularlyLarge(buf_size, BlockSize); + AMS_ASSERT(pooled_buffer.GetSize() > 0 && util::IsAligned(pooled_buffer.GetSize(), BlockSize)); + + /* Read and decrypt in chunks. */ + while (remaining_size > 0) { + size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size); + u8 *dst = static_cast<u8 *>(buf) + cur_offset; + + this->decrypt_function(pooled_buffer.GetBuffer(), cur_size, this->key_index, enc_key, enc_key_size, ctr, IvSize, dst, cur_size); + + std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size); + + cur_offset += cur_size; + remaining_size -= cur_size; + + if (remaining_size > 0) { + AddCounter(ctr, IvSize, cur_size / BlockSize); + } + } + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_alignment_matching_storage_impl.cpp b/libraries/libstratosphere/source/fssystem/fssystem_alignment_matching_storage_impl.cpp new file mode 100644 index 000000000..225a9656d --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_alignment_matching_storage_impl.cpp @@ -0,0 +1,256 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + template<typename T> + constexpr ALWAYS_INLINE size_t GetRoundDownDifference(T x, size_t align) { + return static_cast<size_t>(x - util::AlignDown(x, align)); + } + + template<typename T> + constexpr ALWAYS_INLINE size_t GetRoundUpDifference(T x, size_t align) { + return static_cast<size_t>(util::AlignUp(x, align) - x); + } + + template<typename T> + ALWAYS_INLINE size_t GetRoundUpDifference(T *x, size_t align) { + return GetRoundUpDifference(reinterpret_cast<uintptr_t>(x), align); + } + + } + + Result AlignmentMatchingStorageImpl::Read(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size) { + /* Check preconditions. */ + AMS_ASSERT(work_buf_size >= data_alignment); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Determine extents. */ + char *aligned_core_buffer; + s64 core_offset; + size_t core_size; + size_t buffer_gap; + size_t offset_gap; + s64 covered_offset; + + const size_t offset_round_up_difference = GetRoundUpDifference(offset, data_alignment); + if (util::IsAligned(reinterpret_cast<uintptr_t>(buffer) + offset_round_up_difference, buffer_alignment)) { + aligned_core_buffer = buffer + offset_round_up_difference; + + core_offset = util::AlignUp(offset, data_alignment); + core_size = (size < offset_round_up_difference) ? 0 : util::AlignDown(size - offset_round_up_difference, data_alignment); + buffer_gap = 0; + offset_gap = 0; + + covered_offset = core_size > 0 ? core_offset : offset; + } else { + const size_t buffer_round_up_difference = GetRoundUpDifference(buffer, buffer_alignment); + + aligned_core_buffer = buffer + buffer_round_up_difference; + + core_offset = util::AlignDown(offset, data_alignment); + core_size = (size < buffer_round_up_difference) ? 0 : util::AlignDown(size - buffer_round_up_difference, data_alignment); + buffer_gap = buffer_round_up_difference; + offset_gap = GetRoundDownDifference(offset, data_alignment); + } + + /* Read the core portion. */ + if (core_size > 0) { + R_TRY(base_storage->Read(core_offset, aligned_core_buffer, core_size)); + + if (offset_gap != 0 || buffer_gap != 0) { + std::memmove(aligned_core_buffer - buffer_gap, aligned_core_buffer + offset_gap, core_size - offset_gap); + core_size -= offset_gap; + } + } + + /* Handle the head portion. */ + if (offset < covered_offset) { + const s64 head_offset = util::AlignDown(offset, data_alignment); + const size_t head_size = static_cast<size_t>(covered_offset - offset); + + AMS_ASSERT(GetRoundDownDifference(offset, data_alignment) + head_size <= work_buf_size); + + R_TRY(base_storage->Read(head_offset, work_buf, data_alignment)); + std::memcpy(buffer, work_buf + GetRoundDownDifference(offset, data_alignment), head_size); + } + + /* Handle the tail portion. */ + s64 tail_offset = covered_offset + core_size; + size_t remaining_tail_size = static_cast<size_t>((offset + size) - tail_offset); + while (remaining_tail_size > 0) { + const auto aligned_tail_offset = util::AlignDown(tail_offset, data_alignment); + const auto cur_size = std::min(static_cast<size_t>(aligned_tail_offset + data_alignment - tail_offset), remaining_tail_size); + R_TRY(base_storage->Read(aligned_tail_offset, work_buf, data_alignment)); + + AMS_ASSERT((tail_offset - offset) + cur_size <= size); + AMS_ASSERT((tail_offset - aligned_tail_offset) + cur_size <= data_alignment); + std::memcpy(static_cast<char *>(buffer) + (tail_offset - offset), work_buf + (tail_offset - aligned_tail_offset), cur_size); + + remaining_tail_size -= cur_size; + tail_offset += cur_size; + } + + return ResultSuccess(); + } + + Result AlignmentMatchingStorageImpl::Write(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size) { + /* Check preconditions. */ + AMS_ASSERT(work_buf_size >= data_alignment); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Determine extents. */ + const char *aligned_core_buffer; + s64 core_offset; + size_t core_size; + s64 covered_offset; + + const size_t offset_round_up_difference = GetRoundUpDifference(offset, data_alignment); + if (util::IsAligned(reinterpret_cast<uintptr_t>(buffer) + offset_round_up_difference, buffer_alignment)) { + aligned_core_buffer = buffer + offset_round_up_difference; + + core_offset = util::AlignUp(offset, data_alignment); + core_size = (size < offset_round_up_difference) ? 0 : util::AlignDown(size - offset_round_up_difference, data_alignment); + + covered_offset = core_size > 0 ? core_offset : offset; + } else { + aligned_core_buffer = nullptr; + + core_offset = util::AlignDown(offset, data_alignment); + core_size = 0; + + covered_offset = offset; + } + + /* Write the core portion. */ + if (core_size > 0) { + R_TRY(base_storage->Write(core_offset, aligned_core_buffer, core_size)); + } + + /* Handle the head portion. */ + if (offset < covered_offset) { + const s64 head_offset = util::AlignDown(offset, data_alignment); + const size_t head_size = static_cast<size_t>(covered_offset - offset); + + AMS_ASSERT((offset - head_offset) + head_size <= data_alignment); + + R_TRY(base_storage->Read(head_offset, work_buf, data_alignment)); + std::memcpy(work_buf + (offset - head_offset), buffer, head_size); + R_TRY(base_storage->Write(head_offset, work_buf, data_alignment)); + } + + /* Handle the tail portion. */ + s64 tail_offset = covered_offset + core_size; + size_t remaining_tail_size = static_cast<size_t>((offset + size) - tail_offset); + while (remaining_tail_size > 0) { + AMS_ASSERT(static_cast<size_t>(tail_offset - offset) < size); + + const auto aligned_tail_offset = util::AlignDown(tail_offset, data_alignment); + const auto cur_size = std::min(static_cast<size_t>(aligned_tail_offset + data_alignment - tail_offset), remaining_tail_size); + + R_TRY(base_storage->Read(aligned_tail_offset, work_buf, data_alignment)); + std::memcpy(work_buf + GetRoundDownDifference(tail_offset, data_alignment), buffer + (tail_offset - offset), cur_size); + R_TRY(base_storage->Write(aligned_tail_offset, work_buf, data_alignment)); + + remaining_tail_size -= cur_size; + tail_offset += cur_size; + } + + return ResultSuccess(); + } + + template<> + Result AlignmentMatchingStorageInBulkRead<1>::Read(s64 offset, void *buffer, size_t size) { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); + + /* Determine extents. */ + const auto offset_end = offset + static_cast<s64>(size); + const auto aligned_offset = util::AlignDown(offset, this->data_align); + const auto aligned_offset_end = util::AlignUp(offset_end, this->data_align); + const auto aligned_size = static_cast<size_t>(aligned_offset_end - aligned_offset); + + /* If we aren't aligned, we need to allocate a buffer. */ + PooledBuffer pooled_buffer; + if (aligned_offset != offset || aligned_size != size) { + if (aligned_size <= pooled_buffer.GetAllocatableSizeMax()) { + pooled_buffer.Allocate(aligned_size, this->data_align); + + if (aligned_size <= pooled_buffer.GetSize()) { + R_TRY(this->base_storage->Read(aligned_offset, pooled_buffer.GetBuffer(), aligned_size)); + std::memcpy(buffer, pooled_buffer.GetBuffer() + (offset - aligned_offset), size); + return ResultSuccess(); + } else { + pooled_buffer.Shrink(this->data_align); + } + } else { + pooled_buffer.Allocate(this->data_align, this->data_align); + } + + AMS_ASSERT(pooled_buffer.GetSize() >= static_cast<size_t>(this->data_align)); + } + + /* Determine read extents for the aligned portion. */ + const auto core_offset = util::AlignUp(offset, this->data_align); + const auto core_offset_end = util::AlignDown(offset_end, this->data_align); + + /* Handle any data before the aligned portion. */ + if (offset < core_offset) { + const auto head_size = static_cast<size_t>(core_offset - offset); + AMS_ASSERT(head_size < size); + R_TRY(this->base_storage->Read(aligned_offset, pooled_buffer.GetBuffer(), this->data_align)); + std::memcpy(buffer, pooled_buffer.GetBuffer() + (offset - aligned_offset), head_size); + } + + /* Handle the aligned portion. */ + if (core_offset < core_offset_end) { + const auto core_buffer = static_cast<char *>(buffer) + (core_offset - offset); + const auto core_size = static_cast<size_t>(core_offset_end - core_offset); + + R_TRY(this->base_storage->Read(core_offset, core_buffer, core_size)); + } + + /* Handle any data after the aligned portion. */ + if (core_offset_end < offset_end) { + const auto tail_size = static_cast<size_t>(offset_end - core_offset_end); + R_TRY(this->base_storage->Read(core_offset_end, pooled_buffer.GetBuffer(), this->data_align)); + std::memcpy(buffer, pooled_buffer.GetBuffer(), tail_size); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_allocator_utility.cpp b/libraries/libstratosphere/source/fssystem/fssystem_allocator_utility.cpp new file mode 100644 index 000000000..8a8151537 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_allocator_utility.cpp @@ -0,0 +1,65 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + constexpr bool UseDefaultAllocators = false; + + bool g_used_default_allocator; + + void *DefaultAllocate(size_t size) { + g_used_default_allocator = true; + return std::malloc(size); + } + + void DefaultDeallocate(void *ptr, size_t size) { + std::free(ptr); + } + + AllocateFunction g_allocate_func = UseDefaultAllocators ? DefaultAllocate : nullptr; + DeallocateFunction g_deallocate_func = UseDefaultAllocators ? DefaultDeallocate : nullptr; + + } + + void *Allocate(size_t size) { + AMS_ASSERT(g_allocate_func != nullptr); + return g_allocate_func(size); + } + + void Deallocate(void *ptr, size_t size) { + AMS_ASSERT(g_deallocate_func != nullptr); + return g_deallocate_func(ptr, size); + } + + void InitializeAllocator(AllocateFunction allocate_func, DeallocateFunction deallocate_func) { + AMS_ASSERT(allocate_func != nullptr); + AMS_ASSERT(deallocate_func != nullptr); + + if constexpr (UseDefaultAllocators) { + AMS_ASSERT(g_used_default_allocator == false); + } else { + AMS_ASSERT(g_allocate_func == nullptr); + AMS_ASSERT(g_deallocate_func == nullptr); + } + + g_allocate_func = allocate_func; + g_deallocate_func = deallocate_func; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp new file mode 100644 index 000000000..a8f8046e6 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp @@ -0,0 +1,544 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + using Node = impl::BucketTreeNode<const s64 *>; + static_assert(sizeof(Node) == sizeof(BucketTree::NodeHeader)); + static_assert(std::is_pod<Node>::value); + + constexpr inline s32 NodeHeaderSize = sizeof(BucketTree::NodeHeader); + + class StorageNode { + private: + class Offset { + public: + using difference_type = s64; + private: + s64 offset; + s32 stride; + public: + constexpr Offset(s64 offset, s32 stride) : offset(offset), stride(stride) { /* ... */ } + + constexpr Offset &operator++() { this->offset += this->stride; return *this; } + constexpr Offset operator++(int) { Offset ret(*this); this->offset += this->stride; return ret; } + + constexpr Offset &operator--() { this->offset -= this->stride; return *this; } + constexpr Offset operator--(int) { Offset ret(*this); this->offset -= this->stride; return ret; } + + constexpr difference_type operator-(const Offset &rhs) const { return (this->offset - rhs.offset) / this->stride; } + + constexpr Offset operator+(difference_type ofs) const { return Offset(this->offset + ofs * this->stride, this->stride); } + constexpr Offset operator-(difference_type ofs) const { return Offset(this->offset - ofs * this->stride, this->stride); } + + constexpr Offset &operator+=(difference_type ofs) { this->offset += ofs * this->stride; return *this; } + constexpr Offset &operator-=(difference_type ofs) { this->offset -= ofs * this->stride; return *this; } + + constexpr bool operator==(const Offset &rhs) const { return this->offset == rhs.offset; } + constexpr bool operator!=(const Offset &rhs) const { return this->offset != rhs.offset; } + + constexpr s64 Get() const { return this->offset; } + }; + private: + const Offset start; + const s32 count; + s32 index; + public: + StorageNode(size_t size, s32 count) : start(NodeHeaderSize, static_cast<s32>(size)), count(count), index(-1) { /* ... */ } + StorageNode(s64 ofs, size_t size, s32 count) : start(NodeHeaderSize + ofs, static_cast<s32>(size)), count(count), index(-1) { /* ... */ } + + s32 GetIndex() const { return this->index; } + + void Find(const char *buffer, s64 virtual_address) { + s32 end = this->count; + auto pos = this->start; + + while (end > 0) { + auto half = end / 2; + auto mid = pos + half; + + s64 offset = 0; + std::memcpy(std::addressof(offset), buffer + mid.Get(), sizeof(s64)); + + if (offset <= virtual_address) { + pos = mid + 1; + end -= half + 1; + } else { + end = half; + } + } + + this->index = static_cast<s32>(pos - this->start) - 1; + } + + Result Find(fs::SubStorage &storage, s64 virtual_address) { + s32 end = this->count; + auto pos = this->start; + + while (end > 0) { + auto half = end / 2; + auto mid = pos + half; + + s64 offset = 0; + R_TRY(storage.Read(mid.Get(), std::addressof(offset), sizeof(s64))); + + if (offset <= virtual_address) { + pos = mid + 1; + end -= half + 1; + } else { + end = half; + } + } + + this->index = static_cast<s32>(pos - this->start) - 1; + return ResultSuccess(); + } + }; + + } + + void BucketTree::Header::Format(s32 entry_count) { + AMS_ASSERT(entry_count >= 0); + + this->magic = Magic; + this->version = Version; + this->entry_count = entry_count; + this->reserved = 0; + } + + Result BucketTree::Header::Verify() const { + R_UNLESS(this->magic == Magic, fs::ResultInvalidBucketTreeSignature()); + R_UNLESS(this->entry_count >= 0, fs::ResultInvalidBucketTreeEntryCount()); + R_UNLESS(this->version <= Version, fs::ResultUnsupportedVersion()); + return ResultSuccess(); + } + + Result BucketTree::NodeHeader::Verify(s32 node_index, size_t node_size, size_t entry_size) const { + R_UNLESS(this->index == node_index, fs::ResultInvalidArgument()); + R_UNLESS(entry_size == 0 || node_size < entry_size + NodeHeaderSize, fs::ResultInvalidSize()); + + const size_t max_entry_count = (node_size - NodeHeaderSize) / entry_size; + R_UNLESS(this->count > 0 && static_cast<size_t>(this->count) <= max_entry_count, fs::ResultInvalidBucketTreeNodeEntryCount()); + R_UNLESS(this->offset > 0, fs::ResultInvalidBucketTreeNodeOffset()); + + return ResultSuccess(); + } + + Result BucketTree::Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, size_t node_size, size_t entry_size, s32 entry_count) { + /* Validate preconditions. */ + AMS_ASSERT(allocator != nullptr); + AMS_ASSERT(entry_size >= sizeof(s64)); + AMS_ASSERT(node_size >= entry_size + sizeof(NodeHeader)); + AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); + AMS_ASSERT(util::IsPowerOfTwo(node_size)); + AMS_ASSERT(!this->IsInitialized()); + + /* Ensure valid entry count. */ + R_UNLESS(entry_count > 0, fs::ResultInvalidArgument()); + + /* Allocate node. */ + R_UNLESS(this->node_l1.Allocate(allocator, node_size), fs::ResultBufferAllocationFailed()); + auto node_guard = SCOPE_GUARD { this->node_l1.Free(node_size); }; + + /* Read node. */ + R_TRY(node_storage.Read(0, this->node_l1.Get(), node_size)); + + /* Verify node. */ + R_TRY(this->node_l1->Verify(0, node_size, sizeof(s64))); + + /* Validate offsets. */ + const auto offset_count = GetOffsetCount(node_size); + const auto entry_set_count = GetEntrySetCount(node_size, entry_size, entry_count); + const auto * const node = this->node_l1.Get<Node>(); + + s64 start_offset; + if (offset_count < entry_set_count && node->GetCount() < offset_count) { + start_offset = *node->GetEnd(); + } else { + start_offset = *node->GetBegin(); + } + const auto end_offset = node->GetEndOffset(); + + R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset()); + R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset()); + + /* Set member variables. */ + this->node_storage = node_storage; + this->entry_storage = entry_storage; + this->node_size = node_size; + this->entry_size = entry_size; + this->entry_count = entry_count; + this->offset_count = offset_count; + this->entry_set_count = entry_set_count; + this->start_offset = start_offset; + this->end_offset = end_offset; + + /* Cancel guard. */ + node_guard.Cancel(); + return ResultSuccess(); + } + + void BucketTree::Initialize(size_t node_size, s64 end_offset) { + AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); + AMS_ASSERT(util::IsPowerOfTwo(node_size)); + AMS_ASSERT(end_offset > 0); + AMS_ASSERT(!this->IsInitialized()); + + this->node_size = node_size; + this->end_offset = end_offset; + } + + void BucketTree::Finalize() { + if (this->IsInitialized()) { + this->node_storage = fs::SubStorage(); + this->entry_storage = fs::SubStorage(); + this->node_l1.Free(this->node_size); + this->node_size = 0; + this->entry_size = 0; + this->entry_count = 0; + this->offset_count = 0; + this->entry_set_count = 0; + this->start_offset = 0; + this->end_offset = 0; + } + } + + Result BucketTree::Find(Visitor *visitor, s64 virtual_address) const { + AMS_ASSERT(visitor != nullptr); + AMS_ASSERT(this->IsInitialized()); + + R_UNLESS(virtual_address >= 0, fs::ResultInvalidOffset()); + R_UNLESS(!this->IsEmpty(), fs::ResultOutOfRange()); + + R_TRY(visitor->Initialize(this)); + + return visitor->Find(virtual_address); + } + + Result BucketTree::InvalidateCache() { + /* Invalidate the node storage cache. */ + { + s64 storage_size; + R_TRY(this->node_storage.GetSize(std::addressof(storage_size))); + R_TRY(this->node_storage.OperateRange(fs::OperationId::InvalidateCache, 0, storage_size)); + } + + /* Refresh start/end offsets. */ + { + /* Read node. */ + R_TRY(node_storage.Read(0, this->node_l1.Get(), this->node_size)); + + /* Verify node. */ + R_TRY(this->node_l1->Verify(0, this->node_size, sizeof(s64))); + + /* Validate offsets. */ + const auto * const node = this->node_l1.Get<Node>(); + + s64 start_offset; + if (offset_count < this->entry_set_count && node->GetCount() < this->offset_count) { + start_offset = *node->GetEnd(); + } else { + start_offset = *node->GetBegin(); + } + const auto end_offset = node->GetEndOffset(); + + R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset()); + R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset()); + + /* Set refreshed offsets. */ + this->start_offset = start_offset; + this->end_offset = end_offset; + } + + /* Invalidate the entry storage cache. */ + { + s64 storage_size; + R_TRY(this->entry_storage.GetSize(std::addressof(storage_size))); + R_TRY(this->entry_storage.OperateRange(fs::OperationId::InvalidateCache, 0, storage_size)); + } + + return ResultSuccess(); + } + + Result BucketTree::Visitor::Initialize(const BucketTree *tree) { + AMS_ASSERT(tree != nullptr); + AMS_ASSERT(this->tree == nullptr || this->tree == tree); + + if (this->entry == nullptr) { + this->entry = tree->GetAllocator()->Allocate(tree->entry_size); + R_UNLESS(this->entry != nullptr, fs::ResultBufferAllocationFailed()); + + this->tree = tree; + } + + return ResultSuccess(); + } + + Result BucketTree::Visitor::MoveNext() { + R_UNLESS(this->IsValid(), fs::ResultOutOfRange()); + + /* Invalidate our index, and read the header for the next index. */ + auto entry_index = this->entry_index + 1; + if (entry_index == this->entry_set.info.count) { + const auto entry_set_index = this->entry_set.info.index + 1; + R_UNLESS(entry_set_index < this->entry_set_count, fs::ResultOutOfRange()); + + this->entry_index = -1; + + const auto end = this->entry_set.info.end; + + const auto entry_set_size = this->tree->node_size; + const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); + + R_TRY(this->tree->entry_storage.Read(entry_set_offset, std::addressof(this->entry_set), sizeof(EntrySetHeader))); + R_TRY(this->entry_set.header.Verify(entry_set_index, entry_set_size, this->tree->entry_size)); + + R_UNLESS(this->entry_set.info.start == end && this->entry_set.info.start < this->entry_set.info.end, fs::ResultInvalidBucketTreeEntrySetOffset()); + + entry_index = 0; + } else { + this->entry_index = 1; + } + + /* Read the new entry. */ + const auto entry_size = this->tree->entry_size; + const auto entry_offset = impl::GetBucketTreeEntryOffset(this->entry_set.info.index, this->tree->node_size, entry_size, entry_index); + R_TRY(this->tree->entry_storage.Read(entry_offset, std::addressof(this->entry), entry_size)); + + /* Note that we changed index. */ + this->entry_index = entry_index; + return ResultSuccess(); + } + + Result BucketTree::Visitor::MovePrevious() { + R_UNLESS(this->IsValid(), fs::ResultOutOfRange()); + + /* Invalidate our index, and read the heasder for the previous index. */ + auto entry_index = this->entry_index; + if (entry_index == 0) { + R_UNLESS(this->entry_set.info.index > 0, fs::ResultOutOfRange()); + + this->entry_index = -1; + + const auto start = this->entry_set.info.start; + + const auto entry_set_size = this->tree->node_size; + const auto entry_set_index = this->entry_set.info.index - 1; + const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); + + R_TRY(this->tree->entry_storage.Read(entry_set_offset, std::addressof(this->entry_set), sizeof(EntrySetHeader))); + R_TRY(this->entry_set.header.Verify(entry_set_index, entry_set_size, this->tree->entry_size)); + + R_UNLESS(this->entry_set.info.end == start && this->entry_set.info.start < this->entry_set.info.end, fs::ResultInvalidBucketTreeEntrySetOffset()); + + entry_index = this->entry_set.info.count; + } else { + this->entry_index = -1; + } + + --entry_index; + + /* Read the new entry. */ + const auto entry_size = this->tree->entry_size; + const auto entry_offset = impl::GetBucketTreeEntryOffset(this->entry_set.info.index, this->tree->node_size, entry_size, entry_index); + R_TRY(this->tree->entry_storage.Read(entry_offset, std::addressof(this->entry), entry_size)); + + /* Note that we changed index. */ + this->entry_index = entry_index; + return ResultSuccess(); + } + + Result BucketTree::Visitor::Find(s64 virtual_address) { + AMS_ASSERT(this->tree != nullptr); + + /* Get the node. */ + const auto * const node = this->tree->node_l1.Get<Node>(); + R_UNLESS(virtual_address < node->GetEndOffset(), fs::ResultOutOfRange()); + + /* Get the entry set index. */ + s32 entry_set_index = -1; + if (this->tree->IsExistOffsetL2OnL1() && virtual_address < node->GetBeginOffset()) { + const auto start = node->GetEnd(); + const auto end = node->GetBegin() + tree->offset_count; + + auto pos = std::upper_bound(start, end, virtual_address); + R_UNLESS(start < pos, fs::ResultOutOfRange()); + --pos; + + entry_set_index = static_cast<s32>(pos - start); + } else { + const auto start = node->GetBegin(); + const auto end = node->GetEnd(); + + auto pos = std::upper_bound(start, end, virtual_address); + R_UNLESS(start < pos, fs::ResultOutOfRange()); + --pos; + + if (this->tree->IsExistL2()) { + const auto node_index = static_cast<s32>(pos - start); + R_UNLESS(0 <= node_index && node_index < this->tree->offset_count, fs::ResultInvalidBucketTreeNodeOffset()); + + R_TRY(this->FindEntrySet(std::addressof(entry_set_index), virtual_address, node_index)); + } else { + entry_set_index = static_cast<s32>(pos - start); + } + } + + /* Validate the entry set index. */ + R_UNLESS(0 <= entry_set_index && entry_set_index < this->tree->entry_set_count, fs::ResultInvalidBucketTreeNodeOffset()); + + /* Find the entry. */ + R_TRY(this->FindEntry(virtual_address, entry_set_index)); + + /* Set count. */ + this->entry_set_count = this->tree->entry_set_count; + return ResultSuccess(); + } + + Result BucketTree::Visitor::FindEntrySet(s32 *out_index, s64 virtual_address, s32 node_index) { + const auto node_size = this->tree->node_size; + + PooledBuffer pool(node_size, 1); + if (node_size <= pool.GetSize()) { + return this->FindEntrySetWithBuffer(out_index, virtual_address, node_index, pool.GetBuffer()); + } else { + pool.Deallocate(); + return this->FindEntrySetWithoutBuffer(out_index, virtual_address, node_index); + } + } + + Result BucketTree::Visitor::FindEntrySetWithBuffer(s32 *out_index, s64 virtual_address, s32 node_index, char *buffer) { + /* Calculate node extents. */ + const auto node_size = this->tree->node_size; + const auto node_offset = (node_index + 1) * static_cast<s64>(node_size); + fs::SubStorage &storage = tree->node_storage; + + /* Read the node. */ + R_TRY(storage.Read(node_offset, buffer, node_size)); + + /* Validate the header. */ + NodeHeader header; + std::memcpy(std::addressof(header), buffer, NodeHeaderSize); + R_TRY(header.Verify(node_index, node_size, sizeof(s64))); + + /* Create the node, and find. */ + StorageNode node(sizeof(s64), header.count); + node.Find(buffer, virtual_address); + R_UNLESS(node.GetIndex() >= 0, fs::ResultOutOfRange()); + + /* Return the index. */ + *out_index = this->tree->GetEntrySetIndex(header.index, node.GetIndex()); + return ResultSuccess(); + } + + Result BucketTree::Visitor::FindEntrySetWithoutBuffer(s32 *out_index, s64 virtual_address, s32 node_index) { + /* Calculate node extents. */ + const auto node_size = this->tree->node_size; + const auto node_offset = (node_index + 1) * static_cast<s64>(node_size); + fs::SubStorage &storage = tree->node_storage; + + /* Read and validate the header. */ + NodeHeader header; + R_TRY(storage.Read(node_offset, std::addressof(header), NodeHeaderSize)); + R_TRY(header.Verify(node_index, node_size, sizeof(s64))); + + /* Create the node, and find. */ + StorageNode node(node_offset, sizeof(s64), header.count); + R_TRY(node.Find(storage, virtual_address)); + R_UNLESS(node.GetIndex() >= 0, fs::ResultOutOfRange()); + + /* Return the index. */ + *out_index = this->tree->GetEntrySetIndex(header.index, node.GetIndex()); + return ResultSuccess(); + } + + Result BucketTree::Visitor::FindEntry(s64 virtual_address, s32 entry_set_index) { + const auto entry_set_size = this->tree->node_size; + + PooledBuffer pool(entry_set_size, 1); + if (entry_set_size <= pool.GetSize()) { + return this->FindEntryWithBuffer(virtual_address, entry_set_index, pool.GetBuffer()); + } else { + pool.Deallocate(); + return this->FindEntryWithoutBuffer(virtual_address, entry_set_index); + } + } + + Result BucketTree::Visitor::FindEntryWithBuffer(s64 virtual_address, s32 entry_set_index, char *buffer) { + /* Calculate entry set extents. */ + const auto entry_size = this->tree->entry_size; + const auto entry_set_size = this->tree->node_size; + const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); + fs::SubStorage &storage = tree->node_storage; + + /* Read the entry set. */ + R_TRY(storage.Read(entry_set_offset, buffer, entry_set_size)); + + /* Validate the entry_set. */ + EntrySetHeader entry_set; + std::memcpy(std::addressof(entry_set), buffer, sizeof(EntrySetHeader)); + R_TRY(entry_set.header.Verify(entry_set_index, entry_set_size, entry_size)); + + /* Create the node, and find. */ + StorageNode node(entry_size, entry_set.info.count); + node.Find(buffer, virtual_address); + R_UNLESS(node.GetIndex() >= 0, fs::ResultOutOfRange()); + + /* Copy the data into entry. */ + const auto entry_index = node.GetIndex(); + const auto entry_offset = impl::GetBucketTreeEntryOffset(0, entry_size, entry_index); + std::memcpy(this->entry, buffer + entry_offset, entry_size); + + /* Set our entry set/index. */ + this->entry_set = entry_set; + this->entry_index = entry_index; + + return ResultSuccess(); + } + + Result BucketTree::Visitor::FindEntryWithoutBuffer(s64 virtual_address, s32 entry_set_index) { + /* Calculate entry set extents. */ + const auto entry_size = this->tree->entry_size; + const auto entry_set_size = this->tree->node_size; + const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); + fs::SubStorage &storage = tree->node_storage; + + /* Read and validate the entry_set. */ + EntrySetHeader entry_set; + R_TRY(storage.Read(entry_set_offset, std::addressof(entry_set), sizeof(EntrySetHeader))); + R_TRY(entry_set.header.Verify(entry_set_index, entry_set_size, entry_size)); + + /* Create the node, and find. */ + StorageNode node(entry_set_offset, entry_size, entry_set.info.count); + R_TRY(node.Find(storage, virtual_address)); + R_UNLESS(node.GetIndex() >= 0, fs::ResultOutOfRange()); + + /* Copy the data into entry. */ + const auto entry_index = node.GetIndex(); + const auto entry_offset = impl::GetBucketTreeEntryOffset(entry_set_offset, entry_size, entry_index); + R_TRY(storage.Read(entry_offset, this->entry, entry_size)); + + /* Set our entry set/index. */ + this->entry_set = entry_set; + this->entry_index = entry_index; + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_crypto_configuration.cpp b/libraries/libstratosphere/source/fssystem/fssystem_crypto_configuration.cpp new file mode 100644 index 000000000..76be00e1d --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_crypto_configuration.cpp @@ -0,0 +1,232 @@ +/* + * 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 <stratosphere.hpp> +#include "fssystem_key_slot_cache.hpp" + +namespace ams::fssystem { + + namespace { + + constexpr inline const size_t KeySize = crypto::AesDecryptor128::KeySize; + + constexpr inline const size_t AcidSignatureKeyGenerationMax = 1; + + constexpr inline const u8 AcidSignatureKeyModulusDev[AcidSignatureKeyGenerationMax + 1][AcidSignatureKeyModulusSize] = { + { + 0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89, + 0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87, + 0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C, + 0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B, + 0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5, + 0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32, + 0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53, + 0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4, + 0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA, + 0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B, + 0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F, + 0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33, + 0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C, + 0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3, + 0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0, + 0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69 + }, + { + 0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0, + 0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E, + 0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70, + 0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A, + 0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76, + 0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD, + 0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F, + 0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE, + 0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D, + 0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7, + 0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03, + 0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62, + 0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A, + 0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2, + 0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36, + 0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B + } + }; + + constexpr inline const u8 AcidSignatureKeyModulusProd[AcidSignatureKeyGenerationMax + 1][AcidSignatureKeyModulusSize] = { + { + 0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, + 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, + 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, + 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, + 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, + 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, + 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, + 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, + 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, + 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, + 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, + 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, + 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, + 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, + 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, + 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD + }, + { + 0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90, + 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, + 0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, + 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E, + 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, + 0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, + 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01, + 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, + 0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, + 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6, + 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, + 0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, + 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D, + 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, + 0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, + 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03 + } + }; + + static_assert(sizeof(AcidSignatureKeyModulusProd) == sizeof(AcidSignatureKeyModulusDev)); + + constexpr inline const u8 AcidSignatureKeyPublicExponent[] = { + 0x01, 0x00, 0x01 + }; + + NcaCryptoConfiguration g_nca_crypto_configuration_dev; + NcaCryptoConfiguration g_nca_crypto_configuration_prod; + + constexpr inline s32 KeySlotCacheEntryCount = 3; + KeySlotCache g_key_slot_cache; + std::optional<KeySlotCacheEntry> g_key_slot_cache_entry[KeySlotCacheEntryCount]; + + spl::AccessKey &GetNcaKekAccessKey(s32 key_type) { + static spl::AccessKey s_nca_kek_access_key_array[KeyAreaEncryptionKeyCount] = {}; + static spl::AccessKey s_nca_header_kek_access_key = {}; + static spl::AccessKey s_invalid_nca_kek_access_key = {}; + + if (key_type > static_cast<s32>(KeyType::NcaHeaderKey) || IsInvalidKeyTypeValue(key_type)) { + return s_invalid_nca_kek_access_key; + } else if (key_type == static_cast<s32>(KeyType::NcaHeaderKey)) { + return s_nca_header_kek_access_key; + } else { + return s_nca_kek_access_key_array[key_type]; + } + } + + void GenerateNcaKey(void *dst, size_t dst_size, const void *src, size_t src_size, s32 key_type, const NcaCryptoConfiguration &cfg) { + R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, GetNcaKekAccessKey(key_type), src, src_size)); + } + + void DecryptAesCtr(void *dst, size_t dst_size, s32 key_type, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) { + std::unique_ptr<KeySlotCacheAccessor> accessor; + + R_TRY_CATCH(g_key_slot_cache.Find(std::addressof(accessor), enc_key, enc_key_size, key_type)) { + R_CATCH(fs::ResultTargetNotFound) { + R_ABORT_UNLESS(g_key_slot_cache.AllocateHighPriority(std::addressof(accessor), enc_key, enc_key_size, key_type)); + R_ABORT_UNLESS(spl::LoadAesKey(accessor->GetKeySlotIndex(), GetNcaKekAccessKey(key_type), enc_key, enc_key_size)); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + R_ABORT_UNLESS(spl::ComputeCtr(dst, dst_size, accessor->GetKeySlotIndex(), src, src_size, iv, iv_size)); + } + + void DecryptAesCtrForPreparedKey(void *dst, size_t dst_size, s32 key_type, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) { + std::unique_ptr<KeySlotCacheAccessor> accessor; + + key_type = static_cast<s32>(KeyType::NcaExternalKey); + + R_TRY_CATCH(g_key_slot_cache.Find(std::addressof(accessor), enc_key, enc_key_size, key_type)) { + R_CATCH(fs::ResultTargetNotFound) { + R_ABORT_UNLESS(g_key_slot_cache.AllocateHighPriority(std::addressof(accessor), enc_key, enc_key_size, key_type)); + + spl::AccessKey access_key; + AMS_ABORT_UNLESS(enc_key_size == sizeof(access_key)); + std::memcpy(std::addressof(access_key), enc_key, sizeof(access_key)); + R_ABORT_UNLESS(spl::LoadPreparedAesKey(accessor->GetKeySlotIndex(), access_key)); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + R_ABORT_UNLESS(spl::ComputeCtr(dst, dst_size, accessor->GetKeySlotIndex(), src, src_size, iv, iv_size)); + } + + } + + const ::ams::fssystem::NcaCryptoConfiguration *GetNcaCryptoConfiguration(bool prod) { + /* Decide which configuration to use. */ + NcaCryptoConfiguration *cfg = prod ? std::addressof(g_nca_crypto_configuration_prod) : std::addressof(g_nca_crypto_configuration_dev); + std::memcpy(cfg, fssrv::GetDefaultNcaCryptoConfiguration(prod), sizeof(NcaCryptoConfiguration)); + + /* Set the key generation functions. */ + cfg->generate_key = GenerateNcaKey; + cfg->decrypt_aes_ctr = DecryptAesCtr; + cfg->decrypt_aes_ctr_external = DecryptAesCtrForPreparedKey; + cfg->is_plaintext_header_available = !prod; + + return cfg; + } + + void SetUpKekAccessKeys(bool prod) { + /* Get the crypto configuration. */ + const NcaCryptoConfiguration *nca_crypto_cfg = GetNcaCryptoConfiguration(prod); + + /* Setup the nca keys. */ + { + constexpr s32 Option = 0; + + /* Setup the key area encryption keys. */ + for (u8 i = 0; i < NcaCryptoConfiguration::KeyGenerationMax; ++i) { + spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(GetKeyTypeValue(0, i))), nca_crypto_cfg->key_area_encryption_key_source[0], KeySize, i, Option); + spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(GetKeyTypeValue(1, i))), nca_crypto_cfg->key_area_encryption_key_source[1], KeySize, i, Option); + spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(GetKeyTypeValue(2, i))), nca_crypto_cfg->key_area_encryption_key_source[2], KeySize, i, Option); + } + + /* Setup the header encryption key. */ + R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(GetNcaKekAccessKey(static_cast<s32>(KeyType::NcaHeaderKey))), nca_crypto_cfg->header_encryption_key_source, KeySize, 0, Option)); + } + + /* TODO FS-REIMPL: Save stuff. */ + + /* Setup the keyslot cache. */ + for (s32 i = 0; i < KeySlotCacheEntryCount; i++) { + s32 slot_index; + R_ABORT_UNLESS(spl::AllocateAesKeySlot(std::addressof(slot_index))); + g_key_slot_cache_entry[i].emplace(slot_index); + g_key_slot_cache.AddEntry(std::addressof(g_key_slot_cache_entry[i].value())); + } + } + + void InvalidateHardwareAesKey() { + constexpr u8 InvalidKey[KeySize] = {}; + for (s32 i = 0; i < KeySlotCacheEntryCount; ++i) { + std::unique_ptr<KeySlotCacheAccessor> accessor; + R_ABORT_UNLESS(g_key_slot_cache.AllocateHighPriority(std::addressof(accessor), InvalidKey, KeySize, -1 - i)); + } + } + + const u8 *GetAcidSignatureKeyModulus(bool prod, size_t key_generation) { + AMS_ASSERT(key_generation <= AcidSignatureKeyGenerationMax); + const size_t used_keygen = (key_generation % (AcidSignatureKeyGenerationMax + 1)); + return prod ? AcidSignatureKeyModulusProd[used_keygen] : AcidSignatureKeyModulusDev[used_keygen]; + } + + const u8 *GetAcidSignatureKeyPublicExponent() { + return AcidSignatureKeyPublicExponent; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp new file mode 100644 index 000000000..31011c0c1 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp @@ -0,0 +1,152 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + /* TODO: All of this should really be inside fs process, but ams.mitm wants it to. */ + /* How should we handle this? */ + + namespace { + + /* Official FS has a 4.5 MB exp heap, a 6 MB buffer pool, an 8 MB device buffer manager heap, and a 14 MB buffer manager heap. */ + /* We don't need so much memory for ams.mitm (as we're servicing a much more limited context). */ + /* We'll give ourselves a 2.5 MB exp heap, a 2 MB buffer pool, a 2 MB device buffer manager heap, and a 2 MB buffer manager heap. */ + /* These numbers match signed-system-partition safe FS. */ + constexpr size_t ExpHeapSize = 2_MB + 512_KB; + constexpr size_t BufferPoolSize = 2_MB; + constexpr size_t DeviceBufferSize = 2_MB; + constexpr size_t BufferManagerHeapSize = 2_MB; + + constexpr size_t MaxCacheCount = 1024; + constexpr size_t BlockSize = 16_KB; + + alignas(os::MemoryPageSize) u8 g_exp_heap_buffer[ExpHeapSize]; + lmem::HeapHandle g_exp_heap_handle = nullptr; + fssrv::PeakCheckableMemoryResourceFromExpHeap g_exp_allocator(ExpHeapSize); + + void InitializeExpHeap() { + if (g_exp_heap_handle == nullptr) { + g_exp_heap_handle = lmem::CreateExpHeap(g_exp_heap_buffer, ExpHeapSize, lmem::CreateOption_ThreadSafe); + AMS_ABORT_UNLESS(g_exp_heap_handle != nullptr); + g_exp_allocator.SetHeapHandle(g_exp_heap_handle); + } + } + + void *AllocateForFileSystemProxy(size_t size) { + AMS_ABORT_UNLESS(g_exp_heap_handle != nullptr); + + auto scoped_lock = g_exp_allocator.GetScopedLock(); + + void *p = lmem::AllocateFromExpHeap(g_exp_heap_handle, size); + g_exp_allocator.OnAllocate(p, size); + return p; + } + + void DeallocateForFileSystemProxy(void *p, size_t size) { + AMS_ABORT_UNLESS(g_exp_heap_handle != nullptr); + + auto scoped_lock = g_exp_allocator.GetScopedLock(); + + g_exp_allocator.OnDeallocate(p, size); + lmem::FreeToExpHeap(g_exp_heap_handle, p); + } + + alignas(os::MemoryPageSize) u8 g_device_buffer[DeviceBufferSize]; + + alignas(os::MemoryPageSize) u8 g_buffer_pool[BufferPoolSize]; + TYPED_STORAGE(mem::StandardAllocator) g_buffer_allocator; + TYPED_STORAGE(fssrv::MemoryResourceFromStandardAllocator) g_allocator; + + /* TODO: Nintendo uses os::SetMemoryHeapSize (svc::SetHeapSize) and os::AllocateMemoryBlock for the BufferManager heap. */ + /* It's unclear how we should handle this in ams.mitm (especially hoping to reuse some logic for fs reimpl). */ + /* Should we be doing the same(?) */ + TYPED_STORAGE(fssystem::FileSystemBufferManager) g_buffer_manager; + alignas(os::MemoryPageSize) u8 g_buffer_manager_heap[BufferManagerHeapSize]; + + /* FileSystem creators. */ + TYPED_STORAGE(fssrv::fscreator::RomFileSystemCreator) g_rom_fs_creator; + TYPED_STORAGE(fssrv::fscreator::PartitionFileSystemCreator) g_partition_fs_creator; + TYPED_STORAGE(fssrv::fscreator::StorageOnNcaCreator) g_storage_on_nca_creator; + + fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {}; + + } + + void InitializeForFileSystemProxy() { + /* TODO FS-REIMPL: fssystem::RegisterServiceContext */ + + /* TODO FS-REIMPL: spl::InitializeForFs(); */ + + /* Determine whether we're prod or dev. */ + bool is_prod = !spl::IsDevelopment(); + bool is_development_function_enabled = spl::IsDevelopmentFunctionEnabled(); + + /* Setup our crypto configuration. */ + SetUpKekAccessKeys(is_prod); + + /* Setup our heap. */ + InitializeExpHeap(); + + /* Initialize buffer allocator. */ + new (GetPointer(g_buffer_allocator)) mem::StandardAllocator(g_buffer_pool, BufferPoolSize); + new (GetPointer(g_allocator)) fssrv::MemoryResourceFromStandardAllocator(GetPointer(g_buffer_allocator)); + + /* Set allocators. */ + fs::SetAllocator(AllocateForFileSystemProxy, DeallocateForFileSystemProxy); + fssystem::InitializeAllocator(AllocateForFileSystemProxy, DeallocateForFileSystemProxy); + + /* Initialize the buffer manager. */ + /* TODO FS-REIMPL: os::AllocateMemoryBlock(...); */ + new (GetPointer(g_buffer_manager)) fssystem::FileSystemBufferManager; + GetReference(g_buffer_manager).Initialize(MaxCacheCount, reinterpret_cast<uintptr_t>(g_buffer_manager_heap), BufferManagerHeapSize, BlockSize); + + /* TODO FS-REIMPL: Memory Report Creators, fssrv::SetMemoryReportCreator */ + + /* TODO FS-REIMPL: Create Pooled Threads, fssystem::RegisterThreadPool. */ + + /* Initialize fs creators. */ + new (GetPointer(g_rom_fs_creator)) fssrv::fscreator::RomFileSystemCreator(GetPointer(g_allocator)); + new (GetPointer(g_partition_fs_creator)) fssrv::fscreator::PartitionFileSystemCreator; + new (GetPointer(g_storage_on_nca_creator)) fssrv::fscreator::StorageOnNcaCreator(GetPointer(g_allocator), *GetNcaCryptoConfiguration(is_prod), is_prod, GetPointer(g_buffer_manager)); + + /* TODO FS-REIMPL: Initialize other creators. */ + + g_fs_creator_interfaces = { + .rom_fs_creator = GetPointer(g_rom_fs_creator), + .partition_fs_creator = GetPointer(g_partition_fs_creator), + .storage_on_nca_creator = GetPointer(g_storage_on_nca_creator), + }; + + /* TODO FS-REIMPL: Sd Card detection, speed emulation. */ + + /* Initialize fssrv. TODO FS-REIMPL: More arguments, more actions taken. */ + fssrv::InitializeForFileSystemProxy(std::addressof(g_fs_creator_interfaces), GetPointer(g_buffer_manager), is_development_function_enabled); + + /* Disable auto-abort in fs library code. */ + /* TODO: fs::SetEnabledAutoAbort(false); */ + + /* TODO FS-REIMPL: Initialize fsp server. */ + + /* NOTE: This is done in fsp server init, normally. */ + fssystem::InitializeBufferPool(reinterpret_cast<char *>(g_device_buffer), DeviceBufferSize); + } + + const ::ams::fssrv::fscreator::FileSystemCreatorInterfaces *GetFileSystemCreatorInterfaces() { + return std::addressof(g_fs_creator_interfaces); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp new file mode 100644 index 000000000..ac88b7cd1 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp @@ -0,0 +1,184 @@ +/* + * 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 <stratosphere.hpp> +#include "fssystem_hierarchical_sha256_storage.hpp" + +namespace ams::fssystem { + + namespace { + + s32 Log2(s32 value) { + AMS_ASSERT(value > 0); + AMS_ASSERT(util::IsPowerOfTwo(value)); + + s32 log = 0; + while ((value >>= 1) > 0) { + ++log; + } + return log; + } + + } + + Result HierarchicalSha256Storage::Initialize(IStorage **base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size) { + /* Validate preconditions. */ + AMS_ASSERT(layer_count == LayerCount); + AMS_ASSERT(util::IsPowerOfTwo(htbs)); + AMS_ASSERT(hash_buf != nullptr); + + /* Set size tracking members. */ + this->hash_target_block_size = htbs; + this->log_size_ratio = Log2(this->hash_target_block_size / HashSize); + + /* Get the base storage size. */ + R_TRY(base_storages[2]->GetSize(std::addressof(this->base_storage_size))); + { + auto size_guard = SCOPE_GUARD { this->base_storage_size = 0; }; + R_UNLESS(this->base_storage_size <= static_cast<s64>(HashSize) << log_size_ratio << log_size_ratio, fs::ResultHierarchicalSha256BaseStorageTooLarge()); + size_guard.Cancel(); + } + + /* Set hash buffer tracking members. */ + this->base_storage = base_storages[2]; + this->hash_buffer = static_cast<char *>(hash_buf); + this->hash_buffer_size = hash_buf_size; + + /* Read the master hash. */ + u8 master_hash[HashSize]; + R_TRY(base_storages[0]->Read(0, master_hash, HashSize)); + + /* Read and validate the data being hashed. */ + s64 hash_storage_size; + R_TRY(base_storages[1]->GetSize(std::addressof(hash_storage_size))); + AMS_ASSERT(util::IsAligned(hash_storage_size, HashSize)); + AMS_ASSERT(hash_storage_size <= this->hash_target_block_size); + AMS_ASSERT(hash_storage_size <= static_cast<s64>(this->hash_buffer_size)); + + R_TRY(base_storages[1]->Read(0, this->hash_buffer, static_cast<size_t>(hash_storage_size))); + + /* Calculate and verify the master hash. */ + u8 calc_hash[HashSize]; + crypto::GenerateSha256Hash(calc_hash, sizeof(calc_hash), this->hash_buffer, static_cast<size_t>(hash_storage_size)); + R_UNLESS(crypto::IsSameBytes(master_hash, calc_hash, HashSize), fs::ResultHierarchicalSha256HashVerificationFailed()); + + return ResultSuccess(); + } + + Result HierarchicalSha256Storage::Read(s64 offset, void *buffer, size_t size) { + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate that we have a buffer to read into. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Validate preconditions. */ + R_UNLESS(util::IsAligned(offset, this->hash_target_block_size), fs::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(size, this->hash_target_block_size), fs::ResultInvalidArgument()); + + /* Read the data. */ + const size_t reduced_size = static_cast<size_t>(std::min<s64>(this->base_storage_size, util::AlignUp(offset + size, this->hash_target_block_size) - offset)); + R_TRY(this->base_storage->Read(offset, buffer, reduced_size)); + + /* Temporarily increase our thread priority. */ + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + + /* Setup tracking variables. */ + auto cur_offset = offset; + auto remaining_size = reduced_size; + while (remaining_size > 0) { + /* Generate the hash of the region we're validating. */ + u8 hash[HashSize]; + const auto cur_size = static_cast<size_t>(std::min<s64>(this->hash_target_block_size, remaining_size)); + crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast<u8 *>(buffer) + (cur_offset - offset), cur_size); + + AMS_ASSERT(static_cast<size_t>(cur_offset >> this->log_size_ratio) < this->hash_buffer_size); + + /* Check the hash. */ + { + std::scoped_lock lk(this->mutex); + auto clear_guard = SCOPE_GUARD { std::memset(buffer, 0, size); }; + + R_UNLESS(crypto::IsSameBytes(hash, std::addressof(this->hash_buffer[cur_offset >> this->log_size_ratio]), HashSize), fs::ResultHierarchicalSha256HashVerificationFailed()); + + clear_guard.Cancel(); + } + + /* Advance. */ + cur_offset += cur_size; + remaining_size -= cur_size; + } + + return ResultSuccess(); + } + + Result HierarchicalSha256Storage::Write(s64 offset, const void *buffer, size_t size) { + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate that we have a buffer to read into. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Validate preconditions. */ + R_UNLESS(util::IsAligned(offset, this->hash_target_block_size), fs::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(size, this->hash_target_block_size), fs::ResultInvalidArgument()); + + /* Setup tracking variables. */ + const size_t reduced_size = static_cast<size_t>(std::min<s64>(this->base_storage_size, util::AlignUp(offset + size, this->hash_target_block_size) - offset)); + auto cur_offset = offset; + auto remaining_size = reduced_size; + while (remaining_size > 0) { + /* Generate the hash of the region we're validating. */ + u8 hash[HashSize]; + const auto cur_size = static_cast<size_t>(std::min<s64>(this->hash_target_block_size, remaining_size)); + { + /* Temporarily increase our thread priority. */ + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast<const u8 *>(buffer) + (cur_offset - offset), cur_size); + } + + /* Write the data. */ + R_TRY(this->base_storage->Write(cur_offset, static_cast<const u8 *>(buffer) + (cur_offset - offset), cur_size)); + + /* Write the hash. */ + { + std::scoped_lock lk(this->mutex); + std::memcpy(std::addressof(this->hash_buffer[cur_offset >> this->log_size_ratio]), hash, HashSize); + } + + /* Advance. */ + cur_offset += cur_size; + remaining_size -= cur_size; + } + + return ResultSuccess(); + } + + Result HierarchicalSha256Storage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate preconditions. */ + R_UNLESS(util::IsAligned(offset, this->hash_target_block_size), fs::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(size, this->hash_target_block_size), fs::ResultInvalidArgument()); + + /* Determine size to use. */ + const auto reduced_size = std::min<s64>(this->base_storage_size, util::AlignUp(offset + size, this->hash_target_block_size) - offset); + + /* Operate on the base storage. */ + return this->base_storage->OperateRange(dst, dst_size, op_id, offset, reduced_size, src, src_size); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp new file mode 100644 index 000000000..5ef4a0082 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp @@ -0,0 +1,57 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> + +namespace ams::fssystem { + + class HierarchicalSha256Storage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(HierarchicalSha256Storage); + NON_MOVEABLE(HierarchicalSha256Storage); + public: + static constexpr s32 LayerCount = 3; + static constexpr size_t HashSize = crypto::Sha256Generator::HashSize; + private: + os::Mutex mutex; + IStorage *base_storage; + s64 base_storage_size; + char *hash_buffer; + size_t hash_buffer_size; + s32 hash_target_block_size; + s32 log_size_ratio; + public: + HierarchicalSha256Storage() : mutex(false) { /* ... */ } + + Result Initialize(IStorage **base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size); + + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result Write(s64 offset, const void *buffer, size_t size) override; + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + + virtual Result GetSize(s64 *out) override { + return this->base_storage->GetSize(out); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInHierarchicalSha256StorageA(); + } + }; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_indirect_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_indirect_storage.cpp new file mode 100644 index 000000000..e9c33646b --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_indirect_storage.cpp @@ -0,0 +1,177 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + Result IndirectStorage::Initialize(IAllocator *allocator, fs::SubStorage table_storage) { + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + R_TRY(table_storage.Read(0, std::addressof(header), sizeof(header))); + R_TRY(header.Verify()); + + /* Determine extents. */ + const auto node_storage_size = QueryNodeStorageSize(header.entry_count); + const auto entry_storage_size = QueryEntryStorageSize(header.entry_count); + const auto node_storage_offset = QueryHeaderStorageSize(); + const auto entry_storage_offset = node_storage_offset + node_storage_size; + + /* Initialize. */ + return this->Initialize(allocator, fs::SubStorage(std::addressof(table_storage), node_storage_offset, node_storage_size), fs::SubStorage(std::addressof(table_storage), entry_storage_offset, entry_storage_size), header.entry_count); + } + + void IndirectStorage::Finalize() { + if (this->IsInitialized()) { + this->table.Finalize(); + for (auto i = 0; i < StorageCount; i++) { + this->data_storage[i] = fs::SubStorage(); + } + } + } + + Result IndirectStorage::GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size) { + /* Validate pre-conditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(size >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Clear the out count. */ + R_UNLESS(out_entry_count != nullptr, fs::ResultNullptrArgument()); + *out_entry_count = 0; + + /* Succeed if there's no range. */ + R_SUCCEED_IF(size == 0); + + /* If we have an output array, we need it to be non-null. */ + R_UNLESS(out_entries != nullptr || entry_count == 0, fs::ResultNullptrArgument()); + + /* Check that our range is valid. */ + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + + /* Find the offset in our tree. */ + BucketTree::Visitor visitor; + R_TRY(this->table.Find(std::addressof(visitor), offset)); + { + const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); + R_UNLESS(0 <= entry_offset && this->table.Includes(entry_offset), fs::ResultInvalidIndirectEntryOffset()); + } + + /* Prepare to loop over entries. */ + const auto end_offset = offset + static_cast<s64>(size); + s32 count = 0; + + auto cur_entry = *visitor.Get<Entry>(); + while (cur_entry.GetVirtualOffset() < end_offset) { + /* Try to write the entry to the out list */ + if (entry_count != 0) { + if (count >= entry_count) { + break; + } + std::memcpy(out_entries + count, std::addressof(cur_entry), sizeof(Entry)); + } + + count++; + + /* Advance. */ + if (visitor.CanMoveNext()) { + R_TRY(visitor.MoveNext()); + cur_entry = *visitor.Get<Entry>(); + } else { + break; + } + } + + /* Write the output count. */ + *out_entry_count = count; + return ResultSuccess(); + } + + Result IndirectStorage::Read(s64 offset, void *buffer, size_t size) { + /* Validate pre-conditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Succeed if there's nothing to read. */ + R_SUCCEED_IF(size == 0); + + /* Ensure that we have a buffer to read to. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + R_TRY(this->OperatePerEntry<true>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { + R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size))); + return ResultSuccess(); + })); + + return ResultSuccess(); + } + + Result IndirectStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + switch (op_id) { + case fs::OperationId::InvalidateCache: + { + if (size > 0) { + /* Validate arguments. */ + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + if (!this->table.IsEmpty()) { + /* Invalidate our table's cache. */ + R_TRY(this->table.InvalidateCache()); + + /* Operate on our entries. */ + R_TRY(this->OperatePerEntry<false>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { + R_TRY(storage->OperateRange(dst, dst_size, op_id, data_offset, cur_size, src, src_size)); + return ResultSuccess(); + })); + } + return ResultSuccess(); + } + return ResultSuccess(); + } + case fs::OperationId::QueryRange: + { + /* Validate that we have an output range info. */ + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize()); + + if (size > 0) { + /* Validate arguments. */ + R_UNLESS(this->table.Includes(offset, size), fs::ResultOutOfRange()); + if (!this->table.IsEmpty()) { + /* Create a new info. */ + fs::QueryRangeInfo merged_info; + merged_info.Clear(); + + /* Operate on our entries. */ + R_TRY(this->OperatePerEntry<false>(offset, size, [=, &merged_info](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { + fs::QueryRangeInfo cur_info; + R_TRY(storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, data_offset, cur_size, src, src_size)); + merged_info.Merge(cur_info); + return ResultSuccess(); + })); + + /* Write the merged info. */ + *reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info; + } + } + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInIndirectStorageC(); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp new file mode 100644 index 000000000..89973aeca --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp @@ -0,0 +1,45 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + Result IntegrityRomFsStorage::Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm) { + /* Validate preconditions. */ + AMS_ASSERT(bm != nullptr); + + /* Set master hash. */ + this->master_hash = master_hash; + this->master_hash_storage = std::make_unique<fs::MemoryStorage>(std::addressof(this->master_hash), sizeof(Hash)); + R_UNLESS(this->master_hash_storage != nullptr, fs::ResultAllocationFailureInIntegrityRomFsStorageA()); + + /* Set the master hash storage. */ + storage_info[0] = fs::SubStorage(this->master_hash_storage.get(), 0, sizeof(Hash)); + + /* Set buffers. */ + for (size_t i = 0; i < util::size(this->buffers.buffers); ++i) { + this->buffers.buffers[i] = bm; + } + + /* Initialize our integrity storage. */ + return this->integrity_storage.Initialize(level_hash_info, storage_info, std::addressof(this->buffers), std::addressof(this->mutex), fs::StorageType_RomFs); + } + + void IntegrityRomFsStorage::Finalize() { + this->integrity_storage.Finalize(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_key_slot_cache.hpp b/libraries/libstratosphere/source/fssystem/fssystem_key_slot_cache.hpp new file mode 100644 index 000000000..89cad629e --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_key_slot_cache.hpp @@ -0,0 +1,133 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> + +namespace ams::fssystem { + + class KeySlotCacheAccessor : public ::ams::fs::impl::Newable { + NON_COPYABLE(KeySlotCacheAccessor); + NON_MOVEABLE(KeySlotCacheAccessor); + private: + std::unique_lock<os::Mutex> lk; + const s32 slot_index; + public: + KeySlotCacheAccessor(s32 idx, std::unique_lock<os::Mutex> &&l) : lk(std::move(l)), slot_index(idx) { /* ... */ } + + s32 GetKeySlotIndex() const { return this->slot_index; } + }; + + class KeySlotCacheEntry : public util::IntrusiveListBaseNode<KeySlotCacheEntry> { + NON_COPYABLE(KeySlotCacheEntry); + NON_MOVEABLE(KeySlotCacheEntry); + public: + static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; + private: + const s32 slot_index; + u8 key1[KeySize]; + s32 key2; + public: + explicit KeySlotCacheEntry(s32 idx) : slot_index(idx), key2(-1) { + std::memset(this->key1, 0, sizeof(this->key1)); + } + + bool Contains(const void *key, size_t key_size, s32 key2) const { + AMS_ASSERT(key_size == KeySize); + return key2 == this->key2 && std::memcmp(this->key1, key, KeySize) == 0; + } + + s32 GetKeySlotIndex() const { return this->slot_index; } + + void SetKey(const void *key, size_t key_size, s32 key2) { + AMS_ASSERT(key_size == KeySize); + std::memcpy(this->key1, key, key_size); + this->key2 = key2; + } + }; + + class KeySlotCache { + NON_COPYABLE(KeySlotCache); + NON_MOVEABLE(KeySlotCache); + private: + using KeySlotCacheEntryList = util::IntrusiveListBaseTraits<KeySlotCacheEntry>::ListType; + private: + os::Mutex mutex; + KeySlotCacheEntryList high_priority_mru_list; + KeySlotCacheEntryList low_priority_mru_list; + public: + constexpr KeySlotCache() : mutex(false), high_priority_mru_list(), low_priority_mru_list() { /* ... */ } + + Result AllocateHighPriority(std::unique_ptr<KeySlotCacheAccessor> *out, const void *key, size_t key_size, s32 key2) { + return this->AllocateFromLru(out, this->high_priority_mru_list, key, key_size, key2); + } + + Result AllocateLowPriority(std::unique_ptr<KeySlotCacheAccessor> *out, const void *key, size_t key_size, s32 key2) { + return this->AllocateFromLru(out, this->high_priority_mru_list, key, key_size, key2); + } + + Result Find(std::unique_ptr<KeySlotCacheAccessor> *out, const void *key, size_t key_size, s32 key2) { + std::unique_lock lk(this->mutex); + + KeySlotCacheEntryList *lists[2] = { std::addressof(this->high_priority_mru_list), std::addressof(this->low_priority_mru_list) }; + for (auto list : lists) { + for (auto it = list->begin(); it != list->end(); ++it) { + if (it->Contains(key, key_size, key2)) { + std::unique_ptr accessor = std::make_unique<KeySlotCacheAccessor>(it->GetKeySlotIndex(), std::move(lk)); + R_UNLESS(accessor != nullptr, fs::ResultAllocationFailure()); + + *out = std::move(accessor); + + this->UpdateMru(list, it); + return ResultSuccess(); + } + } + } + + return fs::ResultTargetNotFound(); + } + + void AddEntry(KeySlotCacheEntry *entry) { + std::unique_lock lk(this->mutex); + this->low_priority_mru_list.push_front(*entry); + } + private: + Result AllocateFromLru(std::unique_ptr<KeySlotCacheAccessor> *out, KeySlotCacheEntryList &dst_list, const void *key, size_t key_size, s32 key2) { + std::unique_lock lk(this->mutex); + + KeySlotCacheEntryList &src_list = this->low_priority_mru_list.empty() ? this->high_priority_mru_list : this->low_priority_mru_list; + AMS_ASSERT(!src_list.empty()); + + auto it = src_list.rbegin(); + std::unique_ptr accessor = std::make_unique<KeySlotCacheAccessor>(it->GetKeySlotIndex(), std::move(lk)); + *out = std::move(accessor); + + it->SetKey(key, key_size, key2); + + auto *entry = std::addressof(*it); + src_list.pop_back(); + dst_list.push_front(*entry); + + return ResultSuccess(); + } + + void UpdateMru(KeySlotCacheEntryList *list, KeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + list->erase(it); + list->push_front(*entry); + } + }; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_lru_list_cache.hpp b/libraries/libstratosphere/source/fssystem/fssystem_lru_list_cache.hpp new file mode 100644 index 000000000..f90f0facd --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_lru_list_cache.hpp @@ -0,0 +1,89 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> + +namespace ams::fssystem { + + template<typename Key, typename Value> + class LruListCache { + NON_COPYABLE(LruListCache); + NON_MOVEABLE(LruListCache); + public: + class Node : public ::ams::fs::impl::Newable { + NON_COPYABLE(Node); + NON_MOVEABLE(Node); + public: + Key key; + Value value; + util::IntrusiveListNode mru_list_node; + public: + explicit Node(const Value &value) : value(value) { /* ... */ } + }; + private: + using MruList = typename util::IntrusiveListMemberTraits<&Node::mru_list_node>::ListType; + private: + MruList mru_list; + public: + constexpr LruListCache() : mru_list() { /* ... */ } + + bool FindValueAndUpdateMru(Value *out, const Key &key) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->key == key) { + *out = it->value; + + this->mru_list.erase(it); + this->mru_list.push_front(*it); + + return true; + } + } + + return false; + } + + std::unique_ptr<Node> PopLruNode() { + AMS_ABORT_UNLESS(!this->mru_list.empty()); + Node *lru = std::addressof(*this->mru_list.rbegin()); + this->mru_list.pop_back(); + + return std::unique_ptr<Node>(lru); + } + + void PushMruNode(std::unique_ptr<Node> &&node, const Key &key) { + node->key = key; + this->mru_list.push_front(*node); + node.release(); + } + + void DeleteAllNodes() { + while (!this->mru_list.empty()) { + Node *lru = std::addressof(*this->mru_list.rbegin()); + this->mru_list.erase(this->mru_list.iterator_to(*lru)); + delete lru; + } + } + + size_t GetSize() const { + return this->mru_list.size(); + } + + bool IsEmpty() const { + return this->mru_list.empty(); + } + }; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp new file mode 100644 index 000000000..171ac9317 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp @@ -0,0 +1,1053 @@ +/* + * 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 <stratosphere.hpp> +#include "fssystem_read_only_block_cache_storage.hpp" +#include "fssystem_hierarchical_sha256_storage.hpp" + +namespace ams::fssystem { + + namespace { + + constexpr inline s32 AesCtrExTableCacheBlockSize = AesCtrCounterExtendedStorage::NodeSize; + constexpr inline s32 AesCtrExTableCacheCount = 8; + constexpr inline s32 IndirectTableCacheBlockSize = IndirectStorage::NodeSize; + constexpr inline s32 IndirectTableCacheCount = 8; + constexpr inline s32 IndirectDataCacheBlockSize = 32_KB; + constexpr inline s32 IndirectDataCacheCount = 16; + constexpr inline s32 SparseTableCacheBlockSize = SparseStorage::NodeSize; + constexpr inline s32 SparseTableCacheCount = 4; + + class BufferHolder { + NON_COPYABLE(BufferHolder); + private: + MemoryResource *allocator; + char *buffer; + size_t buffer_size; + public: + BufferHolder() : allocator(), buffer(), buffer_size() { /* ... */ } + BufferHolder(MemoryResource *a, size_t sz) : allocator(a), buffer(static_cast<char *>(a->Allocate(sz))), buffer_size(sz) { /* ... */ } + ~BufferHolder() { + if (this->buffer != nullptr) { + this->allocator->Deallocate(this->buffer, this->buffer_size); + this->buffer = nullptr; + } + } + + BufferHolder(BufferHolder &&rhs) : allocator(rhs.allocator), buffer(rhs.buffer), buffer_size(rhs.buffer_size) { + rhs.buffer = nullptr; + } + + BufferHolder &operator=(BufferHolder &&rhs) { + if (this != std::addressof(rhs)) { + AMS_ASSERT(this->buffer == nullptr); + this->allocator = rhs.allocator; + this->buffer = rhs.buffer; + this->buffer_size = rhs.buffer_size; + + rhs.buffer = nullptr; + } + return *this; + } + + bool IsValid() const { return this->buffer != nullptr; } + char *Get() const { return this->buffer; } + size_t GetSize() const { return this->buffer_size; } + }; + + template<typename Base, typename Sequence> + class DerivedStorageHolderImpl; + + template<typename Base, std::size_t... Is> + class DerivedStorageHolderImpl<Base, std::index_sequence<Is...>> : public Base { + NON_COPYABLE(DerivedStorageHolderImpl); + public: + using StoragePointer = std::unique_ptr<fs::IStorage>; + + template<size_t N> + using IndexedStoragePointer = StoragePointer; + private: + std::shared_ptr<NcaReader> nca_reader; + std::array<StoragePointer, sizeof...(Is)> storages; + private: + + template<size_t N> + void SetImpl(IndexedStoragePointer<N> &&ptr) { + static_assert(N < sizeof...(Is)); + this->storages[N] = std::move(ptr); + } + public: + DerivedStorageHolderImpl() : Base(), nca_reader(), storages() { /* ... */ } + explicit DerivedStorageHolderImpl(std::shared_ptr<NcaReader> nr) : Base(), nca_reader(nr), storages() { /* ... */ } + + #define DEFINE_CONSTRUCTORS(n) \ + template<AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS##n (T)> \ + explicit DerivedStorageHolderImpl(AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS##n (T, t)) : Base(AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS##n (T, t)), nca_reader(), storages() { /* ... */ } \ + template<AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS##n (T)> \ + explicit DerivedStorageHolderImpl(AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS##n (T, t), std::shared_ptr<NcaReader> nr) : Base(AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS##n (T, t)), nca_reader(nr), storages() { /* ... */ } + + AMS_UTIL_VARIADIC_INVOKE_MACRO(DEFINE_CONSTRUCTORS) + + #undef DEFINE_CONSTRUCTORS + + void Set(IndexedStoragePointer<Is> &&... ptrs) { + (this->SetImpl<Is>(std::forward<IndexedStoragePointer<Is>>(ptrs)), ...); + } + }; + + template<typename Base, size_t N> + using DerivedStorageHolder = DerivedStorageHolderImpl<Base, std::make_index_sequence<N>>; + + template<typename Base, size_t N> + class DerivedStorageHolderWithBuffer : public DerivedStorageHolder<Base, N> { + NON_COPYABLE(DerivedStorageHolderWithBuffer); + private: + using BaseHolder = DerivedStorageHolder<Base, N>; + private: + BufferHolder buffer; + public: + DerivedStorageHolderWithBuffer() : BaseHolder(), buffer() { /* ... */ } + + template<typename... Args> + DerivedStorageHolderWithBuffer(Args &&... args) : BaseHolder(std::forward<Args>(args)...), buffer() { /* ... */ } + + using BaseHolder::Set; + + void Set(BufferHolder &&buf) { + this->buffer = std::move(buf); + } + }; + + class AesCtrStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(AesCtrStorageExternal); + NON_MOVEABLE(AesCtrStorageExternal); + public: + static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize; + static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize; + static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize; + private: + IStorage * const base_storage; + u8 iv[IvSize]; + DecryptAesCtrFunction decrypt_function; + s32 key_index; + u8 encrypted_key[KeySize]; + public: + AesCtrStorageExternal(fs::IStorage *bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx) : base_storage(bs), decrypt_function(df), key_index(kidx) { + AMS_ASSERT(bs != nullptr); + AMS_ASSERT(enc_key_size == KeySize); + AMS_ASSERT(iv != nullptr); + AMS_ASSERT(iv_size == IvSize); + + std::memcpy(this->iv, iv, IvSize); + std::memcpy(this->encrypted_key, enc_key, enc_key_size); + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Allow zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + /* NOTE: For some reason, Nintendo uses InvalidArgument instead of InvalidOffset/InvalidSize here. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidArgument()); + + /* Read the data. */ + R_TRY(this->base_storage->Read(offset, buffer, size)); + + /* Temporarily increase our thread priority. */ + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + + /* Allocate a pooled buffer for decryption. */ + PooledBuffer pooled_buffer; + pooled_buffer.AllocateParticularlyLarge(size, BlockSize); + AMS_ASSERT(pooled_buffer.GetSize() >= BlockSize); + + /* Setup the counter. */ + u8 ctr[IvSize]; + std::memcpy(ctr, this->iv, IvSize); + AddCounter(ctr, IvSize, offset / BlockSize); + + /* Setup tracking. */ + size_t remaining_size = size; + s64 cur_offset = 0; + + while (remaining_size > 0) { + /* Get the current size to process. */ + size_t cur_size = std::min(pooled_buffer.GetSize(), remaining_size); + char *dst = static_cast<char *>(buffer) + cur_offset; + + /* Decrypt into the temporary buffer */ + this->decrypt_function(pooled_buffer.GetBuffer(), cur_size, this->key_index, this->encrypted_key, KeySize, ctr, IvSize, dst, cur_size); + + /* Copy to the destination. */ + std::memcpy(dst, pooled_buffer.GetBuffer(), cur_size); + + /* Update tracking. */ + cur_offset += cur_size; + remaining_size -= cur_size; + + if (remaining_size > 0) { + AddCounter(ctr, IvSize, cur_size / BlockSize); + } + } + + return ResultSuccess(); + } + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + switch (op_id) { + case fs::OperationId::QueryRange: + { + /* Validate that we have an output range info. */ + R_UNLESS(dst != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize()); + + /* Operate on our base storage. */ + R_TRY(this->base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + + /* Add in new flags. */ + fs::QueryRangeInfo new_info; + new_info.Clear(); + new_info.aes_ctr_key_type = static_cast<s32>(this->key_index >= 0 ? fs::AesCtrKeyTypeFlag::InternalKeyForHardwareAes : fs::AesCtrKeyTypeFlag::ExternalKeyForHardwareAes); + } + default: + { + /* Operate on our base storage. */ + R_TRY(this->base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + return ResultSuccess(); + } + } + } + + virtual Result GetSize(s64 *out) override { + return this->base_storage->GetSize(out); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fs::ResultUnsupportedOperationInAesCtrStorageExternalA(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInAesCtrStorageExternalB(); + } + }; + + template<typename F> + class SwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(SwitchStorage); + NON_MOVEABLE(SwitchStorage); + private: + std::unique_ptr<fs::IStorage> true_storage; + std::unique_ptr<fs::IStorage> false_storage; + F truth_function; + private: + ALWAYS_INLINE std::unique_ptr<fs::IStorage> &SelectStorage() { + return this->truth_function() ? this->true_storage : this->false_storage; + } + public: + SwitchStorage(std::unique_ptr<fs::IStorage> &&t, std::unique_ptr<fs::IStorage> &&f, F func) : true_storage(std::move(t)), false_storage(std::move(f)), truth_function(func) { /* ... */ } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + return this->SelectStorage()->Read(offset, buffer, size); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + switch (op_id) { + case fs::OperationId::InvalidateCache: + { + R_TRY(this->true_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + R_TRY(this->false_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + return ResultSuccess(); + } + case fs::OperationId::QueryRange: + { + R_TRY(this->SelectStorage()->OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInSwitchStorageA(); + } + } + + virtual Result GetSize(s64 *out) override { + return this->SelectStorage()->GetSize(out); + } + + virtual Result Flush() override { + return this->SelectStorage()->Flush(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return this->SelectStorage()->Write(offset, buffer, size); + } + + virtual Result SetSize(s64 size) override { + return this->SelectStorage()->SetSize(size); + } + }; + + inline s64 GetFsOffset(const NcaReader &reader, s32 fs_index) { + return static_cast<s64>(reader.GetFsOffset(fs_index)); + } + + inline s64 GetFsEndOffset(const NcaReader &reader, s32 fs_index) { + return static_cast<s64>(reader.GetFsEndOffset(fs_index)); + } + + inline void MakeAesXtsIv(void *ctr, s64 base_offset) { + util::StoreBigEndian<s64>(static_cast<s64 *>(ctr) + 1, base_offset / NcaHeader::XtsBlockSize); + } + + inline bool IsUsingHardwareAesCtrForSpeedEmulation() { + auto mode = fssystem::SpeedEmulationConfiguration::GetSpeedEmulationMode(); + return mode == fs::SpeedEmulationMode::None || mode == fs::SpeedEmulationMode::Slower; + } + + using Sha256DataRegion = NcaFsHeader::Region; + using IntegrityLevelInfo = NcaFsHeader::HashData::IntegrityMetaInfo::LevelHashInfo; + using IntegrityDataInfo = IntegrityLevelInfo::HierarchicalIntegrityVerificationLevelInformation; + + inline const Sha256DataRegion &GetSha256DataRegion(const NcaFsHeader::HashData &hash_data) { + return hash_data.hierarchical_sha256_data.hash_layer_region[1]; + } + + inline const IntegrityDataInfo &GetIntegrityDataInfo(const NcaFsHeader::HashData &hash_data) { + return hash_data.integrity_meta_info.level_hash_info.info[hash_data.integrity_meta_info.level_hash_info.max_layers - 2]; + } + + } + + Result NcaFileSystemDriver::OpenRawStorage(std::shared_ptr<fs::IStorage> *out, s32 fs_index) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); + AMS_ASSERT(this->reader != nullptr); + + /* Get storage extents. */ + const auto storage_offset = GetFsOffset(*this->reader, fs_index); + const auto storage_size = GetFsEndOffset(*this->reader, fs_index) - storage_offset; + R_UNLESS(storage_size > 0, fs::ResultInvalidNcaHeader()); + + /* Allocate a substorage. */ + *out = AllocateShared<DerivedStorageHolder<fs::SubStorage, 0>>(this->reader->GetBodyStorage(), storage_offset, storage_size, this->reader); + R_UNLESS(*out != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + return ResultSuccess(); + } + + Result NcaFileSystemDriver::OpenStorage(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(out_header_reader != nullptr); + AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); + + /* Open a reader with the appropriate option. */ + StorageOption option(out_header_reader, fs_index); + R_TRY(this->OpenStorage(out, std::addressof(option))); + + return ResultSuccess(); + } + + Result NcaFileSystemDriver::OpenStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + AMS_ASSERT(this->reader != nullptr); + + /* Get and validate fs index. */ + const auto fs_index = option->GetFsIndex(); + R_UNLESS(this->reader->HasFsInfo(fs_index), fs::ResultPartitionNotFound()); + + /* Initialize a reader for the fs header. */ + auto &header_reader = option->GetHeaderReader(); + R_TRY(header_reader.Initialize(*this->reader, fs_index)); + + /* Create the storage. */ + std::unique_ptr<fs::IStorage> storage; + { + BaseStorage base_storage; + R_TRY(this->CreateBaseStorage(std::addressof(base_storage), option)); + R_TRY(this->CreateDecryptableStorage(std::addressof(storage), option, std::addressof(base_storage))); + } + R_TRY(this->CreateIndirectStorage(std::addressof(storage), option, std::move(storage))); + R_TRY(this->CreateVerificationStorage(std::addressof(storage), std::move(storage), std::addressof(header_reader))); + + /* Set the output. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::OpenDecryptableStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option, bool indirect_needed) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + AMS_ASSERT(this->reader != nullptr); + + /* Get and validate fs index. */ + const auto fs_index = option->GetFsIndex(); + R_UNLESS(this->reader->HasFsInfo(fs_index), fs::ResultPartitionNotFound()); + + /* Initialize a reader for the fs header. */ + auto &header_reader = option->GetHeaderReader(); + if (!header_reader.IsInitialized()) { + R_TRY(header_reader.Initialize(*this->reader, fs_index)); + } + + /* Create the storage. */ + std::unique_ptr<fs::IStorage> storage; + { + BaseStorage base_storage; + R_TRY(this->CreateBaseStorage(std::addressof(base_storage), option)); + R_TRY(this->CreateDecryptableStorage(std::addressof(storage), option, std::addressof(base_storage))); + } + + /* Set the data storage. */ + { + const auto &patch_info = header_reader.GetPatchInfo(); + s64 data_storage_size = 0; + + if (header_reader.GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx) { + data_storage_size = patch_info.aes_ctr_ex_offset; + } else { + switch (header_reader.GetHashType()) { + case NcaFsHeader::HashType::HierarchicalSha256Hash: + { + const auto ®ion = GetSha256DataRegion(header_reader.GetHashData()); + data_storage_size = region.offset + region.size; + } + break; + case NcaFsHeader::HashType::HierarchicalIntegrityHash: + { + const auto &info = GetIntegrityDataInfo(header_reader.GetHashData()); + data_storage_size = info.offset + info.size; + } + break; + default: + return fs::ResultInvalidNcaFsHeaderHashType(); + } + + data_storage_size = util::AlignUp(data_storage_size, NcaHeader::XtsBlockSize); + } + + /* Set the data storage in option. */ + option->SetDataStorage(storage.get(), data_storage_size); + } + + /* Create the indirect storage if needed. */ + if (indirect_needed) { + R_TRY(this->CreateIndirectStorage(std::addressof(storage), option, std::move(storage))); + } + + /* Set the output. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateBaseStorage(BaseStorage *out, StorageOption *option) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + + /* Get the header reader. */ + const auto fs_index = option->GetFsIndex(); + const auto &header_reader = option->GetHeaderReader(); + + /* Get storage extents. */ + const auto storage_offset = GetFsOffset(*this->reader, fs_index); + const auto storage_size = GetFsEndOffset(*this->reader, fs_index) - storage_offset; + R_UNLESS(storage_size > 0, fs::ResultInvalidNcaHeader()); + + /* Set up the sparse storage if we need to, otherwise use body storage directly. */ + if (header_reader.ExistsSparseLayer()) { + const auto &sparse_info = header_reader.GetSparseInfo(); + + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), sparse_info.bucket.header, sizeof(header)); + R_TRY(header.Verify()); + + /* Create a new holder for the storages. */ + std::unique_ptr storage = std::make_unique<DerivedStorageHolder<SparseStorage, 2>>(this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* If there are no entries, there's nothing to actually do. */ + if (header.entry_count == 0) { + storage->Initialize(storage_size); + } else { + /* Prepare to create the decryptable storage. */ + const auto raw_storage = this->reader->GetBodyStorage(); + const auto raw_storage_offset = sparse_info.physical_offset; + const auto raw_storage_size = sparse_info.GetPhysicalSize(); + + /* Validate that we're within range. */ + s64 body_storage_size = 0; + R_TRY(raw_storage->GetSize(std::addressof(body_storage_size))); + R_UNLESS(raw_storage_offset + raw_storage_size <= body_storage_size, fs::ResultNcaBaseStorageOutOfRangeB()); + + /* Create the decryptable storage. */ + std::unique_ptr<fs::IStorage> decryptable_storage; + { + BaseStorage base_storage(raw_storage, raw_storage_offset, raw_storage_size); + base_storage.SetStorageOffset(raw_storage_offset); + base_storage.SetAesCtrUpperIv(sparse_info.MakeAesCtrUpperIv(header_reader.GetAesCtrUpperIv())); + R_TRY(this->CreateAesCtrStorage(std::addressof(decryptable_storage), std::addressof(base_storage))); + } + + /* Create the table storage. */ + std::unique_ptr table_storage = std::make_unique<save::BufferedStorage>(); + R_UNLESS(table_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the table storage. */ + R_TRY(table_storage->Initialize(fs::SubStorage(decryptable_storage.get(), 0, raw_storage_size), this->buffer_manager, SparseTableCacheBlockSize, SparseTableCacheCount)); + + /* Determine storage extents. */ + const auto node_offset = sparse_info.bucket.offset; + const auto node_size = SparseStorage::QueryNodeStorageSize(header.entry_count); + const auto entry_offset = node_offset + node_size; + const auto entry_size = SparseStorage::QueryEntryStorageSize(header.entry_count); + + /* Initialize the storage. */ + R_TRY(storage->Initialize(this->allocator, fs::SubStorage(table_storage.get(), node_offset, node_size), fs::SubStorage(table_storage.get(), entry_offset, entry_size), header.entry_count)); + + /* Set the data/decryptable storage. */ + storage->SetDataStorage(raw_storage, raw_storage_offset, node_offset); + storage->Set(std::move(decryptable_storage), std::move(table_storage)); + } + + /* Set the sparse storage. */ + option->SetSparseStorage(storage.get()); + + /* Set the out storage. */ + out->SetStorage(std::move(storage)); + } else { + /* Validate that we're within range. */ + s64 body_storage_size; + R_TRY(this->reader->GetBodyStorage()->GetSize(std::addressof(body_storage_size))); + R_UNLESS(storage_offset + storage_size <= body_storage_size, fs::ResultNcaBaseStorageOutOfRangeB()); + + /* Set the out storage. */ + out->SetStorage(this->reader->GetBodyStorage(), storage_offset, storage_size); + } + + /* Set the crypto variables. */ + out->SetStorageOffset(storage_offset); + out->SetAesCtrUpperIv(header_reader.GetAesCtrUpperIv()); + + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateDecryptableStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Get the header reader. */ + const auto &header_reader = option->GetHeaderReader(); + + /* Create the appropriate storage for the encryption type. */ + switch (header_reader.GetEncryptionType()) { + case NcaFsHeader::EncryptionType::None: + *out = base_storage->MakeStorage(); + R_UNLESS(*out != nullptr, fs::ResultAllocationFailureInNew()); + break; + case NcaFsHeader::EncryptionType::AesXts: + R_TRY(this->CreateAesXtsStorage(out, base_storage)); + break; + case NcaFsHeader::EncryptionType::AesCtr: + R_TRY(this->CreateAesCtrStorage(out, base_storage)); + break; + case NcaFsHeader::EncryptionType::AesCtrEx: + R_TRY(this->CreateAesCtrExStorage(out, option, base_storage)); + break; + default: + return fs::ResultInvalidNcaFsHeaderEncryptionType(); + } + + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesXtsStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Create the iv. */ + u8 iv[AesXtsStorage::IvSize] = {}; + MakeAesXtsIv(iv, base_storage->GetStorageOffset()); + + /* Allocate a new raw storage. */ + std::unique_ptr<fs::IStorage> raw_storage = base_storage->MakeStorage(); + R_UNLESS(raw_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Make the aes xts storage. */ + const auto *key1 = this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts1); + const auto *key2 = this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts2); + std::unique_ptr xts_storage = std::make_unique<AesXtsStorage>(raw_storage.get(), key1, key2, AesXtsStorage::KeySize, iv, AesXtsStorage::IvSize, NcaHeader::XtsBlockSize); + R_UNLESS(xts_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Make the out storage. */ + std::unique_ptr storage = std::make_unique<DerivedStorageHolder<AlignmentMatchingStorage<NcaHeader::XtsBlockSize, 1>, 2>>(xts_storage.get(), this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Set the substorages. */ + storage->Set(std::move(raw_storage), std::move(xts_storage)); + + /* Set the output. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesCtrStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Create the iv. */ + u8 iv[AesCtrStorage::IvSize] = {}; + AesCtrStorage::MakeIv(iv, sizeof(iv), base_storage->GetAesCtrUpperIv().value, base_storage->GetStorageOffset()); + + /* Create the raw storage. */ + std::unique_ptr raw_storage = base_storage->MakeStorage(); + + /* Create the decrypt storage. */ + const bool has_external_key = reader->HasExternalDecryptionKey(); + std::unique_ptr<fs::IStorage> decrypt_storage; + if (has_external_key) { + decrypt_storage = std::make_unique<AesCtrStorageExternal>(raw_storage.get(), this->reader->GetExternalDecryptionKey(), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, this->reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1); + R_UNLESS(decrypt_storage != nullptr, fs::ResultAllocationFailureInNew()); + } else { + /* Check if we have a hardware key. */ + const bool has_hardware_key = this->reader->HasInternalDecryptionKeyForAesHardwareSpeedEmulation(); + + /* Create the software decryption storage. */ + std::unique_ptr<fs::IStorage> aes_ctr_sw_storage = std::make_unique<AesCtrStorage>(raw_storage.get(), this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorage::KeySize, iv, AesCtrStorage::IvSize); + R_UNLESS(aes_ctr_sw_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* If we have a hardware key and should use it, make the hardware decryption storage. */ + if (has_hardware_key && !this->reader->IsSoftwareAesPrioritized()) { + std::unique_ptr<fs::IStorage> aes_ctr_hw_storage = std::make_unique<AesCtrStorageExternal>(raw_storage.get(), this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, this->reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(this->reader->GetKeyIndex(), this->reader->GetKeyGeneration())); + R_UNLESS(aes_ctr_hw_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Create the selection storage. */ + decrypt_storage = std::make_unique<SwitchStorage<bool (*)()>>(std::move(aes_ctr_hw_storage), std::move(aes_ctr_sw_storage), IsUsingHardwareAesCtrForSpeedEmulation); + R_UNLESS(decrypt_storage != nullptr, fs::ResultAllocationFailureInNew()); + } else { + /* Otherwise, just use the software decryption storage. */ + decrypt_storage = std::move(aes_ctr_sw_storage); + } + } + + /* Create the storage holder. */ + std::unique_ptr storage = std::make_unique<DerivedStorageHolder<AlignmentMatchingStorage<NcaHeader::CtrBlockSize, 1>, 2>>(decrypt_storage.get(), this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Set the storage holder's storages. */ + storage->Set(std::move(raw_storage), std::move(decrypt_storage)); + + /* Set the out storage. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesCtrExStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Check if indirection is needed. */ + const auto &header_reader = option->GetHeaderReader(); + const auto &patch_info = header_reader.GetPatchInfo(); + + /* Read the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), patch_info.aes_ctr_ex_header, sizeof(header)); + R_TRY(header.Verify()); + + /* Validate patch info extents. */ + R_UNLESS(patch_info.indirect_size > 0, fs::ResultInvalidNcaPatchInfoIndirectSize()); + R_UNLESS(patch_info.aes_ctr_ex_size > 0, fs::ResultInvalidNcaPatchInfoAesCtrExSize()); + + /* Make new base storage. */ + const auto base_storage_offset = base_storage->GetStorageOffset(); + const auto base_storage_size = util::AlignUp(patch_info.aes_ctr_ex_offset + patch_info.aes_ctr_ex_size, NcaHeader::XtsBlockSize); + fs::SubStorage new_base_storage; + R_TRY(base_storage->GetSubStorage(std::addressof(new_base_storage), 0, base_storage_size)); + + /* Create the table storage. */ + std::unique_ptr<fs::IStorage> table_storage; + { + BaseStorage aes_ctr_base_storage(std::addressof(new_base_storage), patch_info.aes_ctr_ex_offset, patch_info.aes_ctr_ex_size); + aes_ctr_base_storage.SetStorageOffset(base_storage_offset + patch_info.aes_ctr_ex_offset); + aes_ctr_base_storage.SetAesCtrUpperIv(header_reader.GetAesCtrUpperIv()); + R_TRY(this->CreateAesCtrStorage(std::addressof(table_storage), std::addressof(aes_ctr_base_storage))); + } + + /* Get the table size. */ + s64 table_size = 0; + R_TRY(table_storage->GetSize(std::addressof(table_size))); + + /* Create the buffered storage. */ + std::unique_ptr buffered_storage = std::make_unique<save::BufferedStorage>(); + R_UNLESS(buffered_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the buffered storage. */ + R_TRY(buffered_storage->Initialize(fs::SubStorage(table_storage.get(), 0, table_size), this->buffer_manager, AesCtrExTableCacheBlockSize, AesCtrExTableCacheCount)); + + /* Create an aligned storage for the buffered storage. */ + using AlignedStorage = AlignmentMatchingStorage<NcaHeader::CtrBlockSize, 1>; + std::unique_ptr aligned_storage = std::make_unique<AlignedStorage>(buffered_storage.get()); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Determine the bucket extents. */ + const auto entry_count = header.entry_count; + const s64 data_offset = 0; + const s64 data_size = patch_info.aes_ctr_ex_offset; + const s64 node_offset = 0; + const s64 node_size = AesCtrCounterExtendedStorage::QueryNodeStorageSize(entry_count); + const s64 entry_offset = node_offset + node_size; + const s64 entry_size = AesCtrCounterExtendedStorage::QueryEntryStorageSize(entry_count); + + /* Create bucket storages. */ + fs::SubStorage data_storage(std::addressof(new_base_storage), data_offset, data_size); + fs::SubStorage node_storage(aligned_storage.get(), node_offset, node_size); + fs::SubStorage entry_storage(aligned_storage.get(), entry_offset, entry_size); + + /* Get the secure value. */ + const auto secure_value = header_reader.GetAesCtrUpperIv().part.secure_value; + + /* Create the aes ctr ex storage. */ + std::unique_ptr<fs::IStorage> aes_ctr_ex_storage; + const bool has_external_key = this->reader->HasExternalDecryptionKey(); + if (has_external_key) { + /* Create the decryptor. */ + std::unique_ptr<AesCtrCounterExtendedStorage::IDecryptor> decryptor; + R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(decryptor), this->reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1)); + + /* Create the aes ctr ex storage. */ + std::unique_ptr impl_storage = std::make_unique<AesCtrCounterExtendedStorage>(); + R_UNLESS(impl_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the aes ctr ex storage. */ + R_TRY(impl_storage->Initialize(this->allocator, this->reader->GetExternalDecryptionKey(), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(decryptor))); + + /* Set the option's aes ctr ex storage. */ + option->SetAesCtrExStorageRaw(impl_storage.get()); + + aes_ctr_ex_storage = std::move(impl_storage); + } else { + /* Check if we have a hardware key. */ + const bool has_hardware_key = this->reader->HasInternalDecryptionKeyForAesHardwareSpeedEmulation(); + + /* Create the software decryptor. */ + std::unique_ptr<AesCtrCounterExtendedStorage::IDecryptor> sw_decryptor; + R_TRY(AesCtrCounterExtendedStorage::CreateSoftwareDecryptor(std::addressof(sw_decryptor))); + + /* Make the software storage. */ + std::unique_ptr sw_storage = std::make_unique<AesCtrCounterExtendedStorage>(); + R_UNLESS(sw_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the software storage. */ + R_TRY(sw_storage->Initialize(this->allocator, this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(sw_decryptor))); + + /* Set the option's aes ctr ex storage. */ + option->SetAesCtrExStorageRaw(sw_storage.get()); + + /* If we have a hardware key and should use it, make the hardware decryption storage. */ + if (has_hardware_key && !this->reader->IsSoftwareAesPrioritized()) { + /* Create the hardware decryptor. */ + std::unique_ptr<AesCtrCounterExtendedStorage::IDecryptor> hw_decryptor; + R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(hw_decryptor), this->reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(this->reader->GetKeyIndex(), this->reader->GetKeyGeneration()))); + + /* Create the hardware storage. */ + std::unique_ptr hw_storage = std::make_unique<AesCtrCounterExtendedStorage>(); + R_UNLESS(hw_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the hardware storage. */ + R_TRY(hw_storage->Initialize(this->allocator, this->reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(hw_decryptor))); + + /* Create the selection storage. */ + std::unique_ptr switch_storage = std::make_unique<SwitchStorage<bool (*)()>>(std::move(hw_storage), std::move(sw_storage), IsUsingHardwareAesCtrForSpeedEmulation); + R_UNLESS(switch_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Set the aes ctr ex storage. */ + aes_ctr_ex_storage = std::move(switch_storage); + } else { + /* Set the aes ctr ex storage. */ + aes_ctr_ex_storage = std::move(sw_storage); + } + } + + /* Create the storage holder. */ + std::unique_ptr storage = std::make_unique<DerivedStorageHolder<AlignedStorage, 5>>(aes_ctr_ex_storage.get(), this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Set the aes ctr ex storages in the option. */ + option->SetAesCtrExTableStorage(table_storage.get()); + option->SetAesCtrExStorage(storage.get()); + + /* Set the storage holder's storages. */ + storage->Set(std::move(base_storage->GetStorage()), std::move(table_storage), std::move(buffered_storage), std::move(aligned_storage), std::move(aes_ctr_ex_storage)); + + /* Set the out storage. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateIndirectStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, std::unique_ptr<fs::IStorage> base_storage) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(option != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Check if indirection is needed. */ + const auto &header_reader = option->GetHeaderReader(); + const auto &patch_info = header_reader.GetPatchInfo(); + + if (!patch_info.HasIndirectTable()) { + *out = std::move(base_storage); + return ResultSuccess(); + } + + /* Read the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), patch_info.indirect_header, sizeof(header)); + R_TRY(header.Verify()); + + /* Determine the storage sizes. */ + const auto node_size = IndirectStorage::QueryNodeStorageSize(header.entry_count); + const auto entry_size = IndirectStorage::QueryEntryStorageSize(header.entry_count); + R_UNLESS(node_size + entry_size <= patch_info.indirect_size, fs::ResultInvalidIndirectStorageSize()); + + /* Open the original storage. */ + std::unique_ptr<fs::IStorage> original_storage; + { + const s32 fs_index = header_reader.GetFsIndex(); + + if (this->original_reader != nullptr && this->original_reader->HasFsInfo(fs_index)) { + NcaFsHeaderReader original_header_reader; + R_TRY(original_header_reader.Initialize(*this->original_reader, fs_index)); + + NcaFileSystemDriver original_driver(this->original_reader, this->allocator, this->buffer_manager); + StorageOption original_option(std::addressof(original_header_reader), fs_index); + + BaseStorage original_base_storage; + R_TRY(original_driver.CreateBaseStorage(std::addressof(original_base_storage), std::addressof(original_option))); + R_TRY(original_driver.CreateDecryptableStorage(std::addressof(original_storage), std::addressof(original_option), std::addressof(original_base_storage))); + } else { + original_storage = std::make_unique<fs::MemoryStorage>(nullptr, 0); + R_UNLESS(original_storage != nullptr, fs::ResultAllocationFailureInNew()); + } + } + + /* Get the original data size. */ + s64 original_data_size = 0; + R_TRY(original_storage->GetSize(std::addressof(original_data_size))); + + /* Get the indirect data size. */ + s64 indirect_data_size = patch_info.indirect_offset; + AMS_ASSERT(util::IsAligned(indirect_data_size, NcaHeader::XtsBlockSize)); + + /* Create the indirect table storage. */ + std::unique_ptr indirect_table_storage = std::make_unique<save::BufferedStorage>(); + R_UNLESS(indirect_table_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the indirect table storage. */ + R_TRY(indirect_table_storage->Initialize(fs::SubStorage(base_storage.get(), indirect_data_size, node_size + entry_size), this->buffer_manager, IndirectTableCacheBlockSize, IndirectTableCacheCount)); + + /* Create the indirect data storage. */ + std::unique_ptr indirect_data_storage = std::make_unique<save::BufferedStorage>(); + R_UNLESS(indirect_data_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the indirect data storage. */ + R_TRY(indirect_data_storage->Initialize(fs::SubStorage(base_storage.get(), 0, indirect_data_size), this->buffer_manager, IndirectDataCacheBlockSize, IndirectDataCacheCount)); + + /* Create the storage holder. */ + std::unique_ptr storage = std::make_unique<DerivedStorageHolder<IndirectStorage, 4>>(this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the storage holder. */ + R_TRY(storage->Initialize(this->allocator, fs::SubStorage(indirect_table_storage.get(), 0, node_size), fs::SubStorage(indirect_table_storage.get(), node_size, entry_size), header.entry_count)); + + /* Set the storage holder's storages. */ + storage->SetStorage(0, original_storage.get(), 0, original_data_size); + storage->SetStorage(1, indirect_table_storage.get(), 0, indirect_data_size); + storage->Set(std::move(base_storage), std::move(original_storage), std::move(indirect_table_storage), std::move(indirect_data_storage)); + + /* Set the indirect storage. */ + option->SetIndirectStorage(storage.get()); + + /* Set the out storage. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(header_reader != nullptr); + + /* Create the appropriate storage for the encryption type. */ + switch (header_reader->GetHashType()) { + case NcaFsHeader::HashType::HierarchicalSha256Hash: + R_TRY(this->CreateSha256Storage(out, std::move(base_storage), header_reader)); + break; + case NcaFsHeader::HashType::HierarchicalIntegrityHash: + R_TRY(this->CreateIntegrityVerificationStorage(out, std::move(base_storage), header_reader)); + break; + default: + return fs::ResultInvalidNcaFsHeaderHashType(); + } + + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateSha256Storage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(header_reader != nullptr); + + /* Define storage types. */ + using VerificationStorage = HierarchicalSha256Storage; + using CacheStorage = ReadOnlyBlockCacheStorage; + using AlignedStorage = AlignmentMatchingStoragePooledBuffer<1>; + using StorageHolder = DerivedStorageHolderWithBuffer<AlignedStorage, 4>; + + /* Get and validate the hash data. */ + auto &hash_data = header_reader->GetHashData().hierarchical_sha256_data; + R_UNLESS(util::IsPowerOfTwo(hash_data.hash_block_size), fs::ResultInvalidHierarchicalSha256BlockSize()); + R_UNLESS(hash_data.hash_layer_count == HierarchicalSha256Storage::LayerCount - 1, fs::ResultInvalidHierarchicalSha256LayerCount()); + + /* Get the regions. */ + const auto &hash_region = hash_data.hash_layer_region[0]; + const auto &data_region = hash_data.hash_layer_region[1]; + + /* Determine buffer sizes. */ + constexpr s32 CacheBlockCount = 2; + const auto hash_buffer_size = static_cast<size_t>(hash_region.size); + const auto cache_buffer_size = CacheBlockCount * hash_data.hash_block_size; + const auto total_buffer_size = hash_buffer_size + cache_buffer_size; + + /* Make a buffer holder. */ + BufferHolder buffer_holder(this->allocator, total_buffer_size); + R_UNLESS(buffer_holder.IsValid(), fs::ResultAllocationFailureInNcaFileSystemDriverI()); + + /* Make the data storage. */ + std::unique_ptr data_storage = std::make_unique<fs::SubStorage>(base_storage.get(), data_region.offset, data_region.size); + R_UNLESS(data_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Make the verification storage. */ + std::unique_ptr verification_storage = std::make_unique<VerificationStorage>(); + R_UNLESS(verification_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Make layer storages. */ + fs::MemoryStorage master_hash_storage(std::addressof(hash_data.fs_data_master_hash), sizeof(Hash)); + fs::SubStorage layer_hash_storage(base_storage.get(), hash_region.offset, hash_region.size); + fs::IStorage *storages[VerificationStorage::LayerCount] = { + std::addressof(master_hash_storage), + std::addressof(layer_hash_storage), + data_storage.get() + }; + + /* Initialize the verification storage. */ + R_TRY(verification_storage->Initialize(storages, VerificationStorage::LayerCount, hash_data.hash_block_size, buffer_holder.Get(), hash_buffer_size)); + + /* Make the cache storage. */ + std::unique_ptr cache_storage = std::make_unique<CacheStorage>(verification_storage.get(), hash_data.hash_block_size, buffer_holder.Get() + hash_buffer_size, cache_buffer_size, CacheBlockCount); + R_UNLESS(cache_storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Make the storage holder. */ + std::unique_ptr storage = std::make_unique<StorageHolder>(cache_storage.get(), hash_data.hash_block_size, this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Set the storage holder's data. */ + storage->Set(std::move(base_storage), std::move(data_storage), std::move(verification_storage), std::move(cache_storage)); + storage->Set(std::move(buffer_holder)); + + /* Set the output. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(header_reader != nullptr); + + /* Define storage types. */ + using VerificationStorage = save::HierarchicalIntegrityVerificationStorage; + using StorageInfo = VerificationStorage::HierarchicalStorageInformation; + using StorageHolder = DerivedStorageHolder<IntegrityRomFsStorage, 1>; + + /* Get and validate the hash data. */ + auto &hash_data = header_reader->GetHashData().integrity_meta_info; + save::HierarchicalIntegrityVerificationInformation level_hash_info; + std::memcpy(std::addressof(level_hash_info), std::addressof(hash_data.level_hash_info), sizeof(level_hash_info)); + + R_UNLESS(save::IntegrityMinLayerCount <= level_hash_info.max_layers, fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount()); + R_UNLESS(level_hash_info.max_layers <= save::IntegrityMaxLayerCount, fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount()); + + /* Create storage info. */ + StorageInfo storage_info; + for (s32 i = 0; i < static_cast<s32>(level_hash_info.max_layers - 2); ++i) { + const auto &layer_info = level_hash_info.info[i]; + storage_info[i + 1] = fs::SubStorage(base_storage.get(), layer_info.offset, layer_info.size); + } + + /* Set the last layer info. */ + const auto &layer_info = level_hash_info.info[level_hash_info.max_layers - 2]; + storage_info.SetDataStorage(fs::SubStorage(base_storage.get(), layer_info.offset, layer_info.size)); + + /* Make the storage holder. */ + std::unique_ptr storage = std::make_unique<StorageHolder>(this->reader); + R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + + /* Initialize the integrity storage. */ + R_TRY(storage->Initialize(level_hash_info, hash_data.master_hash, storage_info, this->buffer_manager)); + + /* Set the storage holder's data. */ + storage->Set(std::move(base_storage)); + + /* Set the output. */ + *out = std::move(storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); + + /* Validate magic. */ + R_UNLESS(reader.GetMagic() == NcaHeader::Magic, fs::ResultUnsupportedVersion()); + + /* Check that the fs header exists. */ + R_UNLESS(reader.HasFsInfo(fs_index), fs::ResultPartitionNotFound()); + + /* Initialize the reader. */ + R_TRY(out->Initialize(reader, fs_index)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp new file mode 100644 index 000000000..32ebf7f3e --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp @@ -0,0 +1,31 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + u8 NcaHeader::GetProperKeyGeneration() const { + return std::max(this->key_generation, this->key_generation_2); + } + bool NcaPatchInfo::HasIndirectTable() const { + return this->indirect_size != 0; + } + + bool NcaPatchInfo::HasAesCtrExTable() const { + return this->aes_ctr_ex_size != 0; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp new file mode 100644 index 000000000..93bcfbaa2 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp @@ -0,0 +1,434 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + constexpr inline u32 SdkAddonVersionMin = 0x000B0000; + + constexpr Result CheckNcaMagic(u32 magic) { + /* Verify the magic is not a deprecated one. */ + R_UNLESS(magic != NcaHeader::Magic0, fs::ResultUnsupportedSdkVersion()); + R_UNLESS(magic != NcaHeader::Magic1, fs::ResultUnsupportedSdkVersion()); + R_UNLESS(magic != NcaHeader::Magic2, fs::ResultUnsupportedSdkVersion()); + + /* Verify the magic is the current one. */ + R_UNLESS(magic == NcaHeader::Magic3, fs::ResultInvalidNcaSignature()); + + return ResultSuccess(); + } + + } + + NcaReader::NcaReader() : shared_base_storage(), header_storage(), body_storage(), decrypt_aes_ctr(), decrypt_aes_ctr_external(), is_software_aes_prioritized(false), header_encryption_type(NcaHeader::EncryptionType::Auto) { + std::memset(std::addressof(this->header), 0, sizeof(this->header)); + std::memset(std::addressof(this->decryption_keys), 0, sizeof(this->decryption_keys)); + std::memset(std::addressof(this->external_decryption_key), 0, sizeof(this->external_decryption_key)); + } + + NcaReader::~NcaReader() { + /* ... */ + } + + Result NcaReader::Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg) { + this->shared_base_storage = base_storage; + return this->Initialize(this->shared_base_storage.get(), crypto_cfg); + } + + Result NcaReader::Initialize(fs::IStorage *base_storage, const NcaCryptoConfiguration &crypto_cfg) { + /* Validate preconditions. */ + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(this->body_storage == nullptr); + R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument()); + + /* Generate keys for header. */ + u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize]; + for (size_t i = 0; i < NcaCryptoConfiguration::HeaderEncryptionKeyCount; i++) { + crypto_cfg.generate_key(header_decryption_keys[i], AesXtsStorage::KeySize, crypto_cfg.header_encrypted_encryption_keys[i], AesXtsStorage::KeySize, static_cast<s32>(KeyType::NcaHeaderKey), crypto_cfg); + } + + /* Create the header storage. */ + const u8 header_iv[AesXtsStorage::IvSize] = {}; + std::unique_ptr<fs::IStorage> work_header_storage = std::make_unique<AesXtsStorage>(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorage::KeySize, header_iv, AesXtsStorage::IvSize, NcaHeader::XtsBlockSize); + R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA()); + + /* Read the header. */ + R_TRY(work_header_storage->Read(0, std::addressof(this->header), sizeof(this->header))); + + /* Validate the magic. */ + if (Result magic_result = CheckNcaMagic(this->header.magic); R_FAILED(magic_result)) { + /* If we're not allowed to use plaintext headers, stop here. */ + R_UNLESS(crypto_cfg.is_plaintext_header_available, magic_result); + + /* Try to use a plaintext header. */ + R_TRY(base_storage->Read(0, std::addressof(this->header), sizeof(this->header))); + R_UNLESS(R_SUCCEEDED(CheckNcaMagic(this->header.magic)), magic_result); + + /* Configure to use the plaintext header. */ + s64 base_storage_size; + R_TRY(base_storage->GetSize(std::addressof(base_storage_size))); + work_header_storage.reset(new fs::SubStorage(base_storage, 0, base_storage_size)); + R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA()); + + this->header_encryption_type = NcaHeader::EncryptionType::None; + } + + /* Validate the fixed key signature. */ + R_UNLESS(this->header.header1_signature_key_generation <= NcaCryptoConfiguration::Header1SignatureKeyGenerationMax, fs::ResultInvalidNcaHeader1SignatureKeyGeneration()); + const u8 *header_1_sign_key_modulus = crypto_cfg.header_1_sign_key_moduli[this->header.header1_signature_key_generation]; + AMS_ABORT_UNLESS(header_1_sign_key_modulus != nullptr); + { + const u8 *sig = this->header.header_sign_1; + const size_t sig_size = NcaHeader::HeaderSignSize; + const u8 *mod = header_1_sign_key_modulus; + const size_t mod_size = NcaCryptoConfiguration::Rsa2048KeyModulusSize; + const u8 *exp = crypto_cfg.header_1_sign_key_public_exponent; + const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize; + const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(std::addressof(this->header.magic))); + const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount; + const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); + R_UNLESS(is_signature_valid, fs::ResultNcaHeaderSignature1VerificationFailed()); + } + + /* Validate the sdk version. */ + R_UNLESS(this->header.sdk_addon_version >= SdkAddonVersionMin, fs::ResultUnsupportedSdkVersion()); + + /* Validate the key index. */ + R_UNLESS(this->header.key_index < NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount, fs::ResultInvalidNcaKeyIndex()); + + /* Check if we have a rights id. */ + constexpr const u8 ZeroRightsId[NcaHeader::RightsIdSize] = {}; + if (crypto::IsSameBytes(ZeroRightsId, this->header.rights_id, NcaHeader::RightsIdSize)) { + /* If we do, then we don't have an external key, so we need to generate decryption keys. */ + crypto_cfg.generate_key(this->decryption_keys[NcaHeader::DecryptionKey_AesCtr], crypto::AesDecryptor128::KeySize, this->header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtr * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize, GetKeyTypeValue(this->header.key_index, this->header.GetProperKeyGeneration()), crypto_cfg); + + /* Copy the hardware speed emulation key. */ + std::memcpy(this->decryption_keys[NcaHeader::DecryptionKey_AesCtrHw], this->header.encrypted_key_area + NcaHeader::DecryptionKey_AesCtrHw * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize); + } + + /* Clear the external decryption key. */ + std::memset(this->external_decryption_key, 0, sizeof(this->external_decryption_key)); + + /* Set our decryptor functions. */ + this->decrypt_aes_ctr = crypto_cfg.decrypt_aes_ctr; + this->decrypt_aes_ctr_external = crypto_cfg.decrypt_aes_ctr_external; + + /* Set our storages. */ + this->header_storage = std::move(work_header_storage); + this->body_storage = base_storage; + + return ResultSuccess(); + } + + fs::IStorage *NcaReader::GetBodyStorage() { + return this->body_storage; + } + + u32 NcaReader::GetMagic() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.magic; + } + + NcaHeader::DistributionType NcaReader::GetDistributionType() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.distribution_type; + } + + NcaHeader::ContentType NcaReader::GetContentType() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.content_type; + } + + u8 NcaReader::GetKeyGeneration() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.GetProperKeyGeneration(); + } + + u8 NcaReader::GetKeyIndex() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.key_index; + } + + u64 NcaReader::GetContentSize() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.content_size; + } + + u64 NcaReader::GetProgramId() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.program_id; + } + + u32 NcaReader::GetContentIndex() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.content_index; + } + + u32 NcaReader::GetSdkAddonVersion() const { + AMS_ASSERT(this->body_storage != nullptr); + return this->header.sdk_addon_version; + } + + void NcaReader::GetRightsId(u8 *dst, size_t dst_size) const { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= NcaHeader::RightsIdSize); + std::memcpy(dst, this->header.rights_id, NcaHeader::RightsIdSize); + } + + bool NcaReader::HasFsInfo(s32 index) const { + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + return this->header.fs_info[index].start_sector != 0 || this->header.fs_info[index].end_sector != 0; + } + + s32 NcaReader::GetFsCount() const { + AMS_ASSERT(this->body_storage != nullptr); + for (s32 i = 0; i < NcaHeader::FsCountMax; i++) { + if (!this->HasFsInfo(i)) { + return i; + } + } + return NcaHeader::FsCountMax; + } + + const Hash &NcaReader::GetFsHeaderHash(s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + return this->header.fs_header_hash[index]; + } + + void NcaReader::GetFsHeaderHash(Hash *dst, s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + AMS_ASSERT(dst != nullptr); + std::memcpy(dst, std::addressof(this->header.fs_header_hash[index]), sizeof(*dst)); + } + + void NcaReader::GetFsInfo(NcaHeader::FsInfo *dst, s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + AMS_ASSERT(dst != nullptr); + std::memcpy(dst, std::addressof(this->header.fs_info[index]), sizeof(*dst)); + } + + u64 NcaReader::GetFsOffset(s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + return NcaHeader::SectorToByte(this->header.fs_info[index].start_sector); + } + + u64 NcaReader::GetFsEndOffset(s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + return NcaHeader::SectorToByte(this->header.fs_info[index].end_sector); + } + + u64 NcaReader::GetFsSize(s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + return NcaHeader::SectorToByte(this->header.fs_info[index].end_sector - this->header.fs_info[index].start_sector); + } + + void NcaReader::GetEncryptedKey(void *dst, size_t size) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(size >= NcaHeader::EncryptedKeyAreaSize); + std::memcpy(dst, this->header.encrypted_key_area, NcaHeader::EncryptedKeyAreaSize); + } + + const void *NcaReader::GetDecryptionKey(s32 index) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::DecryptionKey_Count); + return this->decryption_keys[index]; + } + + bool NcaReader::HasValidInternalKey() const { + constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {}; + for (s32 i = 0; i < NcaHeader::DecryptionKey_Count; i++) { + if (!crypto::IsSameBytes(ZeroKey, this->header.encrypted_key_area + i * crypto::AesDecryptor128::KeySize, crypto::AesDecryptor128::KeySize)) { + return true; + } + } + return false; + } + + bool NcaReader::HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const { + constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {}; + return !crypto::IsSameBytes(ZeroKey, this->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), crypto::AesDecryptor128::KeySize); + } + + bool NcaReader::IsSoftwareAesPrioritized() const { + return this->is_software_aes_prioritized; + } + + void NcaReader::PrioritizeSoftwareAes() { + this->is_software_aes_prioritized = true; + } + + bool NcaReader::HasExternalDecryptionKey() const { + constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {}; + return !crypto::IsSameBytes(ZeroKey, this->GetExternalDecryptionKey(), crypto::AesDecryptor128::KeySize); + } + + const void *NcaReader::GetExternalDecryptionKey() const { + return this->external_decryption_key; + } + + void NcaReader::SetExternalDecryptionKey(const void *src, size_t size) { + AMS_ASSERT(src != nullptr); + AMS_ASSERT(size == sizeof(this->external_decryption_key)); + std::memcpy(this->external_decryption_key, src, sizeof(this->external_decryption_key)); + } + + void NcaReader::GetRawData(void *dst, size_t dst_size) const { + AMS_ASSERT(this->body_storage != nullptr); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= sizeof(NcaHeader)); + + std::memcpy(dst, std::addressof(this->header), sizeof(NcaHeader)); + } + + DecryptAesCtrFunction NcaReader::GetExternalDecryptAesCtrFunction() const { + AMS_ASSERT(this->decrypt_aes_ctr != nullptr); + return this->decrypt_aes_ctr; + } + + DecryptAesCtrFunction NcaReader::GetExternalDecryptAesCtrFunctionForExternalKey() const { + AMS_ASSERT(this->decrypt_aes_ctr_external != nullptr); + return this->decrypt_aes_ctr_external; + } + + NcaHeader::EncryptionType NcaReader::GetEncryptionType() const { + return this->header_encryption_type; + } + + Result NcaReader::ReadHeader(NcaFsHeader *dst, s32 index) const { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + + const s64 offset = sizeof(NcaHeader) + sizeof(NcaFsHeader) * index; + return this->header_storage->Read(offset, dst, sizeof(NcaFsHeader)); + } + + Result NcaReader::VerifyHeaderSign2(const void *mod, size_t mod_size) { + AMS_ASSERT(this->body_storage != nullptr); + constexpr const u8 HeaderSign2KeyPublicExponent[] = { 0x01, 0x00, 0x01 }; + + const u8 *sig = this->header.header_sign_2; + const size_t sig_size = NcaHeader::HeaderSignSize; + const u8 *exp = HeaderSign2KeyPublicExponent; + const size_t exp_size = sizeof(HeaderSign2KeyPublicExponent); + const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(std::addressof(this->header.magic))); + const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount; + const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); + R_UNLESS(is_signature_valid, fs::ResultNcaHeaderSignature2VerificationFailed()); + + return ResultSuccess(); + } + + Result NcaFsHeaderReader::Initialize(const NcaReader &reader, s32 index) { + /* Reset ourselves to uninitialized. */ + this->fs_index = -1; + + /* Read the header. */ + R_TRY(reader.ReadHeader(std::addressof(this->data), index)); + + /* Generate the hash. */ + Hash hash; + crypto::GenerateSha256Hash(std::addressof(hash), sizeof(hash), std::addressof(this->data), sizeof(NcaFsHeader)); + + /* Validate the hash. */ + R_UNLESS(crypto::IsSameBytes(std::addressof(reader.GetFsHeaderHash(index)), std::addressof(hash), sizeof(Hash)), fs::ResultNcaFsHeaderHashVerificationFailed()); + + /* Set our index. */ + this->fs_index = index; + return ResultSuccess(); + } + + void NcaFsHeaderReader::GetRawData(void *dst, size_t dst_size) const { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= sizeof(NcaFsHeader)); + std::memcpy(dst, std::addressof(this->data), sizeof(NcaFsHeader)); + } + + NcaFsHeader::HashData &NcaFsHeaderReader::GetHashData() { + AMS_ASSERT(this->IsInitialized()); + return this->data.hash_data; + } + + const NcaFsHeader::HashData &NcaFsHeaderReader::GetHashData() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.hash_data; + } + + u16 NcaFsHeaderReader::GetVersion() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.version; + } + + s32 NcaFsHeaderReader::GetFsIndex() const { + AMS_ASSERT(this->IsInitialized()); + return this->fs_index; + } + + NcaFsHeader::FsType NcaFsHeaderReader::GetFsType() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.fs_type; + } + + NcaFsHeader::HashType NcaFsHeaderReader::GetHashType() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.hash_type; + } + + NcaFsHeader::EncryptionType NcaFsHeaderReader::GetEncryptionType() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.encryption_type; + } + + NcaPatchInfo &NcaFsHeaderReader::GetPatchInfo() { + AMS_ASSERT(this->IsInitialized()); + return this->data.patch_info; + } + + const NcaPatchInfo &NcaFsHeaderReader::GetPatchInfo() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.patch_info; + } + + const NcaAesCtrUpperIv NcaFsHeaderReader::GetAesCtrUpperIv() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.aes_ctr_upper_iv; + } + + bool NcaFsHeaderReader::ExistsSparseLayer() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.sparse_info.generation != 0; + } + + NcaSparseInfo &NcaFsHeaderReader::GetSparseInfo() { + AMS_ASSERT(this->IsInitialized()); + return this->data.sparse_info; + } + + const NcaSparseInfo &NcaFsHeaderReader::GetSparseInfo() const { + AMS_ASSERT(this->IsInitialized()); + return this->data.sparse_info; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp b/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp new file mode 100644 index 000000000..52445c640 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp @@ -0,0 +1,139 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> +#include "fssystem_lru_list_cache.hpp" + +namespace ams::fssystem { + + class ReadOnlyBlockCacheStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(ReadOnlyBlockCacheStorage); + NON_MOVEABLE(ReadOnlyBlockCacheStorage); + private: + using BlockCache = LruListCache<s64, char *>; + private: + os::Mutex mutex; + BlockCache block_cache; + fs::IStorage * const base_storage; + s32 block_size; + public: + ReadOnlyBlockCacheStorage(IStorage *bs, s32 bsz, char *buf, size_t buf_size, s32 cache_block_count) : mutex(false), base_storage(bs), block_size(bsz) { + /* Validate preconditions. */ + AMS_ASSERT(buf_size >= static_cast<size_t>(this->block_size)); + AMS_ASSERT(util::IsPowerOfTwo(this->block_size)); + AMS_ASSERT(cache_block_count > 0); + AMS_ASSERT(buf_size >= static_cast<size_t>(this->block_size * cache_block_count)); + + /* Create a node for each cache block. */ + for (auto i = 0; i < cache_block_count; i++) { + std::unique_ptr node = std::make_unique<BlockCache::Node>(buf + this->block_size * i); + AMS_ASSERT(node != nullptr); + + if (node != nullptr) { + this->block_cache.PushMruNode(std::move(node), -1); + } + } + } + + ~ReadOnlyBlockCacheStorage() { + this->block_cache.DeleteAllNodes(); + } + + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Validate preconditions. */ + AMS_ASSERT(util::IsAligned(offset, this->block_size)); + AMS_ASSERT(util::IsAligned(size, this->block_size)); + + if (size == static_cast<size_t>(this->block_size)) { + char *cached_buffer = nullptr; + + /* Try to find a cached copy of the data. */ + { + std::scoped_lock lk(this->mutex); + bool found = this->block_cache.FindValueAndUpdateMru(std::addressof(cached_buffer), offset / this->block_size); + if (found) { + std::memcpy(buffer, cached_buffer, size); + return ResultSuccess(); + } + } + + /* We failed to get a cache hit, so read in the data. */ + R_TRY(this->base_storage->Read(offset, buffer, size)); + + /* Add the block to the cache. */ + { + std::scoped_lock lk(this->mutex); + auto lru = this->block_cache.PopLruNode(); + std::memcpy(lru->value, buffer, this->block_size); + this->block_cache.PushMruNode(std::move(lru), offset / this->block_size); + } + + return ResultSuccess(); + } else { + return this->base_storage->Read(offset, buffer, size); + } + } + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Validate preconditions. */ + AMS_ASSERT(util::IsAligned(offset, this->block_size)); + AMS_ASSERT(util::IsAligned(size, this->block_size)); + + /* If invalidating cache, invalidate our blocks. */ + if (op_id == fs::OperationId::InvalidateCache) { + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + + std::scoped_lock lk(this->mutex); + + const size_t cache_block_count = this->block_cache.GetSize(); + BlockCache valid_cache; + + for (size_t count = 0; count < cache_block_count; ++count) { + auto lru = this->block_cache.PopLruNode(); + if (offset <= lru->key && lru->key < offset + size) { + this->block_cache.PushMruNode(std::move(lru), -1); + } else { + valid_cache.PushMruNode(std::move(lru), lru->key); + } + } + + while (!valid_cache.IsEmpty()) { + auto lru = valid_cache.PopLruNode(); + this->block_cache.PushMruNode(std::move(lru), lru->key); + } + } + + /* Operate on the base storage. */ + return this->base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + } + + virtual Result GetSize(s64 *out) override { + return this->base_storage->GetSize(out); + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fs::ResultUnsupportedOperationInReadOnlyBlockCacheStorageA(); + } + + virtual Result SetSize(s64 size) override { + return fs::ResultUnsupportedOperationInReadOnlyBlockCacheStorageB(); + } + }; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_sparse_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_sparse_storage.cpp new file mode 100644 index 000000000..8681c18f3 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_sparse_storage.cpp @@ -0,0 +1,44 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + Result SparseStorage::Read(s64 offset, void *buffer, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(offset >= 0); + AMS_ASSERT(this->IsInitialized()); + + /* Allow zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + if (this->GetEntryTable().IsEmpty()) { + R_UNLESS(this->GetEntryTable().Includes(offset, size), fs::ResultOutOfRange()); + std::memset(buffer, 0, size); + } else { + R_TRY(this->OperatePerEntry<false>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { + R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size))); + return ResultSuccess(); + })); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_speed_emulation_configuration.cpp b/libraries/libstratosphere/source/fssystem/fssystem_speed_emulation_configuration.cpp new file mode 100644 index 000000000..c0b5a7c5e --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_speed_emulation_configuration.cpp @@ -0,0 +1,34 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem { + + namespace { + + std::atomic<::ams::fs::SpeedEmulationMode> g_speed_emulation_mode = ::ams::fs::SpeedEmulationMode::None; + + } + + void SpeedEmulationConfiguration::SetSpeedEmulationMode(::ams::fs::SpeedEmulationMode mode) { + g_speed_emulation_mode = mode; + } + + ::ams::fs::SpeedEmulationMode SpeedEmulationConfiguration::GetSpeedEmulationMode() { + return g_speed_emulation_mode; + } + +} diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_block_cache_buffered_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_block_cache_buffered_storage.cpp new file mode 100644 index 000000000..225171c92 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_block_cache_buffered_storage.cpp @@ -0,0 +1,1234 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem::save { + + BlockCacheBufferedStorage::BlockCacheBufferedStorage() + : buffer_manager(), mutex(), entries(), data_storage(), last_result(ResultSuccess()), data_size(), verification_block_size(), verification_block_shift(), invalidate_index(), max_cache_entry_count(), flags(), buffer_level(-1) + { + /* ... */ + } + + BlockCacheBufferedStorage::~BlockCacheBufferedStorage() { + this->Finalize(); + } + + Result BlockCacheBufferedStorage::Initialize(IBufferManager *bm, os::Mutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, fs::StorageType storage_type) { + /* Validate preconditions. */ + AMS_ASSERT(data != nullptr); + AMS_ASSERT(bm != nullptr); + AMS_ASSERT(mtx != nullptr); + AMS_ASSERT(this->buffer_manager == nullptr); + AMS_ASSERT(this->mutex == nullptr); + AMS_ASSERT(this->data_storage == nullptr); + AMS_ASSERT(this->entries == nullptr); + AMS_ASSERT(max_cache_entries > 0); + + /* Create the entry. */ + this->entries = fs::impl::MakeUnique<CacheEntry[]>(static_cast<size_t>(max_cache_entries)); + R_UNLESS(this->entries != nullptr, fs::ResultAllocationFailureInBlockCacheBufferedStorageA()); + + /* Set members. */ + this->buffer_manager = bm; + this->mutex = mtx; + this->data_storage = data; + this->data_size = data_size; + this->verification_block_size = verif_block_size; + this->last_result = ResultSuccess(); + this->invalidate_index = 0; + this->max_cache_entry_count = max_cache_entries; + this->flags = 0; + this->buffer_level = buffer_level; + this->storage_type = storage_type; + + /* Calculate block shift. */ + this->verification_block_shift = ILog2(static_cast<u32>(verif_block_size)); + AMS_ASSERT(static_cast<size_t>(1ull << this->verification_block_size) == this->verification_block_size); + + /* Clear the entry. */ + std::memset(this->entries.get(), 0, sizeof(CacheEntry) * this->max_cache_entry_count); + + /* Set burst mode. */ + this->SetKeepBurstMode(is_keep_burst_mode); + + /* Set real data cache. */ + this->SetRealDataCache(is_real_data); + + return ResultSuccess(); + } + + void BlockCacheBufferedStorage::Finalize() { + if (this->entries != nullptr) { + /* Invalidate all cache entries. */ + this->InvalidateAllCacheEntries(); + + /* Clear members. */ + this->buffer_manager = nullptr; + this->mutex = nullptr; + this->data_storage = nullptr; + this->data_size = 0; + this->verification_block_size = 0; + this->verification_block_shift = 0; + this->invalidate_index = 0; + this->max_cache_entry_count = 0; + + this->entries.reset(); + } + } + + Result BlockCacheBufferedStorage::Read(s64 offset, void *buffer, size_t size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Determine the extents to read. */ + s64 read_offset = offset; + size_t read_size = size; + + R_UNLESS(read_offset < this->data_size, fs::ResultInvalidOffset()); + + if (static_cast<s64>(read_offset + read_size) > this->data_size) { + read_size = static_cast<size_t>(this->data_size - read_offset); + } + + /* Determine the aligned range to read. */ + const size_t block_alignment = this->verification_block_size; + s64 aligned_offset = util::AlignDown(read_offset, block_alignment); + s64 aligned_offset_end = util::AlignUp(read_offset + read_size, block_alignment); + + AMS_ASSERT(0 <= aligned_offset && aligned_offset_end <= static_cast<s64>(util::AlignUp(this->data_size, block_alignment))); + + /* Try to read using cache. */ + char *dst = static_cast<char *>(buffer); + { + /* Determine if we can do bulk reads. */ + constexpr s64 BulkReadSizeMax = 2_MB; + const bool bulk_read_enabled = (read_offset != aligned_offset || static_cast<s64>(read_offset + read_size) != aligned_offset_end) && aligned_offset_end - aligned_offset <= BulkReadSizeMax; + + /* Read the head cache. */ + CacheEntry head_entry = {}; + MemoryRange head_range = {}; + bool head_cache_needed = true; + R_TRY(this->ReadHeadCache(std::addressof(head_range), std::addressof(head_entry), std::addressof(head_cache_needed), std::addressof(read_offset), std::addressof(aligned_offset), aligned_offset_end, std::addressof(dst), std::addressof(read_size))); + + /* We may be done after reading the head cache, so check if we are. */ + R_SUCCEED_IF(aligned_offset >= aligned_offset_end); + + /* Ensure we destroy the head buffer. */ + auto head_guard = SCOPE_GUARD { this->DestroyBuffer(std::addressof(head_entry), head_range); }; + + /* Read the tail cache. */ + CacheEntry tail_entry = {}; + MemoryRange tail_range = {}; + bool tail_cache_needed = true; + R_TRY(this->ReadTailCache(std::addressof(tail_range), std::addressof(tail_entry), std::addressof(tail_cache_needed), read_offset, aligned_offset, std::addressof(aligned_offset_end), dst, std::addressof(read_size))); + + /* We may be done after reading the tail cache, so check if we are. */ + R_SUCCEED_IF(aligned_offset >= aligned_offset_end); + + /* Ensure that we destroy the tail buffer. */ + auto tail_guard = SCOPE_GUARD { this->DestroyBuffer(std::addressof(tail_entry), tail_range); }; + + /* Try to do a bulk read. */ + if (bulk_read_enabled) { + /* The bulk read will destroy our head/tail buffers. */ + head_guard.Cancel(); + tail_guard.Cancel(); + + do { + /* Do the bulk read. If we fail due to pooled buffer allocation failing, fall back to the normal read codepath. */ + R_TRY_CATCH(this->BulkRead(read_offset, dst, read_size, std::addressof(head_range), std::addressof(tail_range), std::addressof(head_entry), std::addressof(tail_entry), head_cache_needed, tail_cache_needed)) { + R_CATCH(fs::ResultAllocationFailurePooledBufferNotEnoughSize) { break; } + } R_END_TRY_CATCH; + + /* Se successfully did a bulk read, so we're done. */ + return ResultSuccess(); + } while (0); + } + } + + /* Read the data using non-bulk reads. */ + while (aligned_offset < aligned_offset_end) { + /* Ensure that there is data for us to read. */ + AMS_ASSERT(read_size > 0); + + /* If conditions allow us to, read in burst mode. This doesn't use the cache. */ + if (this->IsEnabledKeepBurstMode() && read_offset == aligned_offset && (block_alignment * 2 <= read_size)) { + const size_t aligned_size = util::AlignDown(read_size, block_alignment); + + /* Flush the entries. */ + R_TRY(this->UpdateLastResult(this->FlushRangeCacheEntries(read_offset, aligned_size, false))); + + /* Read the data. */ + R_TRY(this->UpdateLastResult(this->data_storage->Read(read_offset, dst, aligned_size))); + + /* Advance. */ + dst += aligned_size; + read_offset += aligned_size; + read_size -= aligned_size; + aligned_offset += aligned_size; + } else { + /* Get the buffer associated with what we're reading. */ + CacheEntry entry; + MemoryRange range; + R_TRY(this->UpdateLastResult(this->GetAssociateBuffer(std::addressof(range), std::addressof(entry), aligned_offset, static_cast<size_t>(aligned_offset_end - aligned_offset), true))); + + /* Determine where to read data into, and ensure that our entry is aligned. */ + char *src = reinterpret_cast<char *>(range.first); + AMS_ASSERT(util::IsAligned(entry.size, block_alignment)); + + /* If the entry isn't cached, read the data. */ + if (!entry.is_cached) { + if (Result result = this->data_storage->Read(entry.offset, src, entry.size); R_FAILED(result)) { + this->DestroyBuffer(std::addressof(entry), range); + return this->UpdateLastResult(result); + } + entry.is_cached = true; + } + + /* Validate the entry extents. */ + AMS_ASSERT(static_cast<s64>(entry.offset) <= aligned_offset); + AMS_ASSERT(aligned_offset < static_cast<s64>(entry.offset + entry.size)); + AMS_ASSERT(aligned_offset <= read_offset); + + /* Copy the data. */ + { + /* Determine where and how much to copy. */ + const s64 buffer_offset = read_offset - entry.offset; + const size_t copy_size = std::min(read_size, static_cast<size_t>(entry.offset + entry.size - read_offset)); + + /* Actually copy the data. */ + std::memcpy(dst, src + buffer_offset, copy_size); + + /* Advance. */ + dst += copy_size; + read_offset += copy_size; + read_size -= copy_size; + } + + /* Release the cache entry. */ + R_TRY(this->UpdateLastResult(this->StoreOrDestroyBuffer(range, std::addressof(entry)))); + aligned_offset = entry.offset + entry.size; + } + } + + /* Ensure that we read all the data. */ + AMS_ASSERT(read_size == 0); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::Write(s64 offset, const void *buffer, size_t size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Determine the extents to read. */ + R_UNLESS(offset < this->data_size, fs::ResultInvalidOffset()); + + if (static_cast<s64>(offset + size) > this->data_size) { + size = static_cast<size_t>(this->data_size - offset); + } + + /* The actual extents may be zero-size, so succeed if that's the case. */ + R_SUCCEED_IF(size == 0); + + /* Determine the aligned range to read. */ + const size_t block_alignment = this->verification_block_size; + s64 aligned_offset = util::AlignDown(offset, block_alignment); + const s64 aligned_offset_end = util::AlignUp(offset + size, block_alignment); + + AMS_ASSERT(0 <= aligned_offset && aligned_offset_end <= static_cast<s64>(util::AlignUp(this->data_size, block_alignment))); + + /* Write the data. */ + const u8 *src = static_cast<const u8 *>(buffer); + while (aligned_offset < aligned_offset_end) { + /* If conditions allow us to, write in burst mode. This doesn't use the cache. */ + if (this->IsEnabledKeepBurstMode() && offset == aligned_offset && (block_alignment * 2 <= size)) { + const size_t aligned_size = util::AlignDown(size, block_alignment); + + /* Flush the entries. */ + R_TRY(this->UpdateLastResult(this->FlushRangeCacheEntries(offset, aligned_size, true))); + + /* Read the data. */ + R_TRY(this->UpdateLastResult(this->data_storage->Write(offset, src, aligned_size))); + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + + /* Advance. */ + src += aligned_size; + offset += aligned_size; + size -= aligned_size; + aligned_offset += aligned_size; + } else { + /* Get the buffer associated with what we're writing. */ + CacheEntry entry; + MemoryRange range; + R_TRY(this->UpdateLastResult(this->GetAssociateBuffer(std::addressof(range), std::addressof(entry), aligned_offset, static_cast<size_t>(aligned_offset_end - aligned_offset), true))); + + /* Determine where to write data into. */ + char *dst = reinterpret_cast<char *>(range.first); + + /* If the entry isn't cached and we're writing a partial entry, read in the entry. */ + if (!entry.is_cached && ((offset != entry.offset) || (offset + size < entry.offset + entry.size))) { + if (Result result = this->data_storage->Read(entry.offset, dst, entry.size); R_FAILED(result)) { + this->DestroyBuffer(std::addressof(entry), range); + return this->UpdateLastResult(result); + } + } + entry.is_cached = true; + + /* Validate the entry extents. */ + AMS_ASSERT(static_cast<s64>(entry.offset) <= aligned_offset); + AMS_ASSERT(aligned_offset < static_cast<s64>(entry.offset + entry.size)); + AMS_ASSERT(aligned_offset <= offset); + + /* Copy the data. */ + { + /* Determine where and how much to copy. */ + const s64 buffer_offset = offset - entry.offset; + const size_t copy_size = std::min(size, static_cast<size_t>(entry.offset + entry.size - offset)); + + /* Actually copy the data. */ + std::memcpy(dst + buffer_offset, src, copy_size); + + /* Advance. */ + src += copy_size; + offset += copy_size; + size -= copy_size; + } + + /* Set the entry as write-back. */ + entry.is_write_back = true; + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + + /* Store the associated buffer. */ + CacheIndex index; + R_TRY(this->UpdateLastResult(this->StoreAssociateBuffer(std::addressof(index), range, entry))); + + /* If we need to, flush the cache entry. */ + if (index >= 0 && IsEnabledKeepBurstMode() && offset == aligned_offset && (block_alignment * 2 <= size)) { + R_TRY(this->UpdateLastResult(this->FlushCacheEntry(index, false))); + } + } + } + + /* Ensure that didn't end up in a failure state. */ + R_TRY(this->last_result); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::GetSize(s64 *out) { + /* Validate pre-conditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->data_storage != nullptr); + + /* Set the size. */ + *out = this->data_size; + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::Flush() { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Flush all cache entries. */ + R_TRY(this->UpdateLastResult(this->FlushAllCacheEntries())); + + /* Flush the data storage. */ + R_TRY(this->UpdateLastResult(this->data_storage->Flush())); + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + + switch (op_id) { + case fs::OperationId::Clear: + { + R_TRY(this->ClearImpl(offset, size)); + return ResultSuccess(); + } + case fs::OperationId::ClearSignature: + { + R_TRY(this->ClearSignatureImpl(offset, size)); + return ResultSuccess(); + } + case fs::OperationId::InvalidateCache: + { + R_UNLESS(this->storage_type != fs::StorageType_SaveData, fs::ResultUnsupportedOperationInBlockCacheBufferedStorageB()); + R_TRY(this->InvalidateCacheImpl(offset, size)); + return ResultSuccess(); + } + case fs::OperationId::QueryRange: + { + R_TRY(this->QueryRangeImpl(dst, dst_size, offset, size)); + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInBlockCacheBufferedStorageC(); + } + } + + Result BlockCacheBufferedStorage::Commit() { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Flush all cache entries. */ + R_TRY(this->UpdateLastResult(this->FlushAllCacheEntries())); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::OnRollback() { + /* Validate pre-conditions. */ + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Release all valid entries back to the buffer manager. */ + const auto max_cache_entry_count = this->GetMaxCacheEntryCount(); + for (s32 i = 0; i < max_cache_entry_count; i++) { + const auto &entry = this->entries[i]; + if (entry.is_valid) { + if (entry.is_write_back) { + AMS_ASSERT(entry.memory_address != 0 && entry.handle == 0); + this->buffer_manager->DeallocateBuffer(entry.memory_address, entry.memory_size); + } else { + AMS_ASSERT(entry.memory_address == 0 && entry.handle != 0); + const auto memory_range = this->buffer_manager->AcquireCache(entry.handle); + if (memory_range.first != 0) { + this->buffer_manager->DeallocateBuffer(memory_range.first, memory_range.second); + } + } + } + } + + /* Clear all entries. */ + std::memset(this->entries.get(), 0, sizeof(CacheEntry) * max_cache_entry_count); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::ClearImpl(s64 offset, s64 size) { + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Get our storage size. */ + s64 storage_size = 0; + R_TRY(this->GetSize(std::addressof(storage_size))); + + /* Check the access range. */ + R_UNLESS(0 <= offset && offset < storage_size, fs::ResultInvalidOffset()); + + /* Determine the extents to data signature for. */ + auto start_offset = util::AlignDown(offset, this->verification_block_size); + auto end_offset = util::AlignUp(std::min(offset + size, storage_size), this->verification_block_size); + + /* Flush the entries. */ + R_TRY(this->UpdateLastResult(this->FlushRangeCacheEntries(offset, size, true))); + + /* Handle any data before or after the aligned range. */ + if (start_offset < offset || offset + size < end_offset) { + /* Allocate a work buffer. */ + std::unique_ptr<char[], fs::impl::Deleter> work = fs::impl::MakeUnique<char[]>(this->verification_block_size); + R_UNLESS(work != nullptr, fs::ResultAllocationFailureInBlockCacheBufferedStorageB()); + + /* Handle data before the aligned range. */ + if (start_offset < offset) { + /* Read the block. */ + R_TRY(this->UpdateLastResult(this->data_storage->Read(start_offset, work.get(), this->verification_block_size))); + + /* Determine the partial extents to clear. */ + const auto clear_offset = static_cast<size_t>(offset - start_offset); + const auto clear_size = static_cast<size_t>(std::min(static_cast<s64>(this->verification_block_size - clear_offset), size)); + + /* Clear the partial block. */ + std::memset(work.get() + clear_offset, 0, clear_size); + + /* Write the partially cleared block. */ + R_TRY(this->UpdateLastResult(this->data_storage->Write(start_offset, work.get(), this->verification_block_size))); + + /* Update the start offset. */ + start_offset += this->verification_block_size; + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + } + + /* Handle data after the aligned range. */ + if (start_offset < offset + size && offset + size < end_offset) { + /* Read the block. */ + const auto last_offset = end_offset - this->verification_block_size; + R_TRY(this->UpdateLastResult(this->data_storage->Read(last_offset, work.get(), this->verification_block_size))); + + /* Clear the partial block. */ + const auto clear_size = static_cast<size_t>((offset + size) - last_offset); + std::memset(work.get(), 0, clear_size); + + /* Write the partially cleared block. */ + R_TRY(this->UpdateLastResult(this->data_storage->Write(last_offset, work.get(), this->verification_block_size))); + + /* Update the end offset. */ + end_offset -= this->verification_block_size; + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + } + } + + /* We're done if there's no data to clear. */ + R_SUCCEED_IF(start_offset == end_offset); + + /* Clear the signature for the aligned range. */ + R_TRY(this->UpdateLastResult(this->data_storage->OperateRange(fs::OperationId::Clear, start_offset, end_offset - start_offset))); + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::ClearSignatureImpl(s64 offset, s64 size) { + /* Ensure we aren't already in a failed state. */ + R_TRY(this->last_result); + + /* Get our storage size. */ + s64 storage_size = 0; + R_TRY(this->GetSize(std::addressof(storage_size))); + + /* Check the access range. */ + R_UNLESS(0 <= offset && offset < storage_size, fs::ResultInvalidOffset()); + + /* Determine the extents to clear signature for. */ + const auto start_offset = util::AlignUp(offset, this->verification_block_size); + const auto end_offset = util::AlignDown(std::min(offset + size, storage_size), this->verification_block_size); + + /* Flush the entries. */ + R_TRY(this->UpdateLastResult(this->FlushRangeCacheEntries(offset, size, true))); + + /* Clear the signature for the aligned range. */ + R_TRY(this->UpdateLastResult(this->data_storage->OperateRange(fs::OperationId::ClearSignature, start_offset, end_offset - start_offset))); + + /* Set blocking buffer manager allocations. */ + buffers::EnableBlockingBufferManagerAllocation(); + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::InvalidateCacheImpl(s64 offset, s64 size) { + /* Invalidate the entries corresponding to the range. */ + /* NOTE: Nintendo does not check the result of this invalidation. */ + this->InvalidateRangeCacheEntries(offset, size); + + /* Get our storage size. */ + s64 storage_size = 0; + R_TRY(this->GetSize(std::addressof(storage_size))); + + /* Determine the extents we can actually query. */ + const auto actual_size = std::min(size, storage_size - offset); + const auto aligned_offset = util::AlignDown(offset, this->verification_block_size); + const auto aligned_offset_end = util::AlignUp(offset + actual_size, this->verification_block_size); + const auto aligned_size = aligned_offset_end - aligned_offset; + + /* Invalidate the aligned range. */ + { + Result result = this->data_storage->OperateRange(fs::OperationId::InvalidateCache, aligned_offset, aligned_size); + AMS_ASSERT(!fs::ResultBufferAllocationFailed::Includes(result)); + R_TRY(result); + } + + /* Clear our last result if we should. */ + if (fs::ResultIntegrityVerificationStorageCorrupted::Includes(this->last_result)) { + this->last_result = ResultSuccess(); + } + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::QueryRangeImpl(void *dst, size_t dst_size, s64 offset, s64 size) { + /* Get our storage size. */ + s64 storage_size = 0; + R_TRY(this->GetSize(std::addressof(storage_size))); + + /* Determine the extents we can actually query. */ + const auto actual_size = std::min(size, storage_size - offset); + const auto aligned_offset = util::AlignDown(offset, this->verification_block_size); + const auto aligned_offset_end = util::AlignUp(offset + actual_size, this->verification_block_size); + const auto aligned_size = aligned_offset_end - aligned_offset; + + /* Query the aligned range. */ + R_TRY(this->UpdateLastResult(this->data_storage->OperateRange(dst, dst_size, fs::OperationId::QueryRange, aligned_offset, aligned_size, nullptr, 0))); + + return ResultSuccess(); + } + + bool BlockCacheBufferedStorage::ExistsRedundantCacheEntry(const CacheEntry &entry) const { + /* Get the entry's extents. */ + const s64 offset = entry.offset; + const size_t size = entry.size; + + /* Lock our mutex. */ + std::scoped_lock lk(*this->mutex); + + /* Iterate over all entries, checking if any overlap our extents. */ + const auto max_cache_entry_count = this->GetMaxCacheEntryCount(); + for (auto i = 0; i < max_cache_entry_count; ++i) { + const auto &entry = this->entries[i]; + if (entry.is_valid && (entry.is_write_back ? entry.memory_address != 0 : entry.handle != 0)) { + if (entry.offset < static_cast<s64>(offset + size) && offset < static_cast<s64>(entry.offset + entry.size)) { + return true; + } + } + } + + return false; + } + + Result BlockCacheBufferedStorage::GetAssociateBuffer(MemoryRange *out_range, CacheEntry *out_entry, s64 offset, size_t ideal_size, bool is_allocate_for_write) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + AMS_ASSERT(out_range != nullptr); + AMS_ASSERT(out_entry != nullptr); + + /* Lock our mutex. */ + std::scoped_lock lk(*this->mutex); + + /* Get the maximum cache entry count. */ + const CacheIndex max_cache_entry_count = static_cast<CacheIndex>(this->GetMaxCacheEntryCount()); + + /* Locate the index of the cache entry, if present. */ + CacheIndex index; + size_t actual_size = ideal_size; + for (index = 0; index < max_cache_entry_count; ++index) { + const auto &entry = this->entries[index]; + if (entry.is_valid && (entry.is_write_back ? entry.memory_address != 0 : entry.handle != 0)) { + const s64 entry_offset = entry.offset; + if (entry_offset <= offset && offset < static_cast<s64>(entry_offset + entry.size)) { + break; + } + + if (offset <= entry_offset && entry_offset < static_cast<s64>(offset + actual_size)) { + actual_size = static_cast<s64>(entry_offset - offset); + } + } + } + + /* Clear the out range. */ + out_range->first = 0; + out_range->second = 0; + + /* If we located an entry, use it. */ + if (index != max_cache_entry_count) { + auto &entry = this->entries[index]; + + /* Get the range of the found entry. */ + if (entry.is_write_back) { + *out_range = std::make_pair(entry.memory_address, entry.memory_size); + } else { + *out_range = this->buffer_manager->AcquireCache(entry.handle); + } + + /* Get the found entry. */ + *out_entry = entry; + AMS_ASSERT(out_entry->is_valid); + AMS_ASSERT(out_entry->is_cached); + + /* Clear the entry in the cache. */ + entry.is_valid = false; + entry.handle = 0; + entry.memory_address = 0; + entry.memory_size = 0; + + /* Set the output entry. */ + out_entry->is_valid = true; + out_entry->handle = 0; + out_entry->memory_address = 0; + out_entry->memory_size = 0; + } + + /* If we don't have an out entry, allocate one. */ + if (out_range->first == 0) { + /* Ensure that the allocatable size is above a threshold. */ + const auto size_threshold = this->buffer_manager->GetTotalSize() / 8; + if (this->buffer_manager->GetTotalAllocatableSize() < size_threshold) { + R_TRY(this->FlushAllCacheEntries()); + } + + /* Decide in advance on a block alignment. */ + const size_t block_alignment = this->verification_block_size; + + /* Ensure that the size we request is valid. */ + { + AMS_ASSERT(actual_size >= 1); + actual_size = std::min(actual_size, block_alignment * 2); + } + AMS_ASSERT(actual_size >= block_alignment); + + /* Allocate a buffer. */ + R_TRY(buffers::AllocateBufferUsingBufferManagerContext(out_range, this->buffer_manager, actual_size, IBufferManager::BufferAttribute(this->buffer_level), [=](const MemoryRange &buffer) { + return buffer.first != 0 && block_alignment <= buffer.second; + }, AMS_CURRENT_FUNCTION_NAME)); + + /* Ensure our size is accurate. */ + actual_size = std::min(actual_size, out_range->second); + + /* Set the output entry. */ + out_entry->is_valid = true; + out_entry->is_write_back = false; + out_entry->is_cached = false; + out_entry->is_flushing = false; + out_entry->handle = false; + out_entry->memory_address = 0; + out_entry->memory_size = 0; + out_entry->offset = offset; + out_entry->size = actual_size; + } + + /* Ensure that we ended up with a coherent out range. */ + AMS_ASSERT(out_range->second >= out_entry->size); + + return ResultSuccess(); + } + + void BlockCacheBufferedStorage::DestroyBuffer(CacheEntry *entry, const MemoryRange &range) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->buffer_manager != nullptr); + AMS_ASSERT(entry != nullptr); + + /* Set the entry as invalid and not cached. */ + entry->is_cached = false; + entry->is_valid = false; + + /* Release the entry. */ + this->buffer_manager->DeallocateBuffer(range.first, range.second); + } + + Result BlockCacheBufferedStorage::StoreAssociateBuffer(CacheIndex *out, const MemoryRange &range, const CacheEntry &entry) { + /* Validate pre-conditions. */ + AMS_ASSERT(out != nullptr); + + /* Lock our mutex. */ + std::scoped_lock lk(*this->mutex); + + /* If the entry is write-back, ensure we don't exceed certain dirtiness thresholds. */ + if (entry.is_write_back) { + R_TRY(this->ControlDirtiness()); + } + + /* Get the maximum cache entry count. */ + const CacheIndex max_cache_entry_count = static_cast<CacheIndex>(this->GetMaxCacheEntryCount()); + AMS_ASSERT(max_cache_entry_count > 0); + + /* Locate the index of an unused cache entry. */ + CacheIndex index; + for (index = 0; index < max_cache_entry_count; ++index) { + if (!this->entries[index].is_valid) { + break; + } + } + + /* If all entries are valid, we need to invalidate one. */ + if (index == max_cache_entry_count) { + /* Increment the index to invalidate. */ + this->invalidate_index = (this->invalidate_index + 1) % max_cache_entry_count; + + /* Get the entry to invalidate. */ + const CacheEntry *entry_to_invalidate = std::addressof(this->entries[this->invalidate_index]); + + /* Ensure that the entry can be invalidated. */ + AMS_ASSERT(entry_to_invalidate->is_valid); + AMS_ASSERT(!entry_to_invalidate->is_flushing); + + /* Invalidate the entry. */ + R_TRY(this->FlushCacheEntry(this->invalidate_index, true)); + + /* Check that the entry was invalidated successfully. */ + AMS_ASSERT(!entry_to_invalidate->is_valid); + AMS_ASSERT(!entry_to_invalidate->is_flushing); + + index = this->invalidate_index; + } + + /* Store the entry. */ + CacheEntry *entry_ptr = std::addressof(this->entries[index]); + *entry_ptr = entry; + + /* Assert that the entry is valid to store. */ + AMS_ASSERT(entry_ptr->is_valid); + AMS_ASSERT(entry_ptr->is_cached); + AMS_ASSERT(entry_ptr->handle == 0); + AMS_ASSERT(entry_ptr->memory_address == 0); + + /* Ensure that the new entry isn't redundant. */ + if (!ExistsRedundantCacheEntry(*entry_ptr)) { + /* Store the cache's buffer. */ + if (entry_ptr->is_write_back) { + entry_ptr->handle = 0; + entry_ptr->memory_address = range.first; + entry_ptr->memory_size = range.second; + } else { + entry_ptr->handle = this->buffer_manager->RegisterCache(range.first, range.second, IBufferManager::BufferAttribute(this->buffer_level)); + entry_ptr->memory_address = 0; + entry_ptr->memory_size = 0; + } + + /* Set the out index. */ + AMS_ASSERT(entry_ptr->is_valid); + *out = index; + this->invalidate_index = index; + } else { + /* If a redundant entry exists, we don't need the newly stored entry. */ + this->buffer_manager->DeallocateBuffer(range.first, range.second); + entry_ptr->is_valid = false; + *out = -1; + } + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::FlushCacheEntry(CacheIndex index, bool invalidate) { + /* Lock our mutex. */ + std::scoped_lock lk(*this->mutex); + + /* Get the entry. */ + CacheEntry *entry = std::addressof(this->entries[index]); + MemoryRange memory_range; + + /* Check that the entry's state allows for flush. */ + AMS_ASSERT(entry->is_valid); + AMS_ASSERT(!entry->is_flushing); + + /* If we're not write back (i.e. an invalidate is happening), just release the buffer. */ + if (!entry->is_write_back) { + AMS_ASSERT(invalidate); + + /* Get and release the buffer. */ + memory_range = this->buffer_manager->AcquireCache(entry->handle); + if (memory_range.first != 0) { + this->buffer_manager->DeallocateBuffer(memory_range.first, memory_range.second); + } + + /* The entry is no longer valid. */ + entry->is_valid = false; + + return ResultSuccess(); + } + + /* Note that we've started flushing. */ + entry->is_flushing = true; + + /* Create and check our memory range. */ + memory_range = std::make_pair(entry->memory_address, entry->memory_size); + AMS_ASSERT(memory_range.first != 0); + AMS_ASSERT(memory_range.second >= entry->size); + + /* Validate the entry's offset. */ + AMS_ASSERT(entry->offset >= 0); + AMS_ASSERT(entry->offset < this->data_size); + AMS_ASSERT(util::IsAligned(entry->offset, this->verification_block_size)); + + /* Write back the data. */ + Result result = ResultSuccess(); + size_t write_size = entry->size; + if (R_SUCCEEDED(this->last_result)) { + /* Set blocking buffer manager allocations. */ + result = this->data_storage->Write(entry->offset, reinterpret_cast<const void *>(memory_range.first), write_size); + + /* Check the result. */ + AMS_ASSERT(!fs::ResultBufferAllocationFailed::Includes(result)); + } else { + result = this->last_result; + } + + /* Set that we're not write-back. */ + entry->is_write_back = false; + + /* If we're invalidating, release the buffer. Otherwise, register the flushed data. */ + if (invalidate) { + this->buffer_manager->DeallocateBuffer(memory_range.first, memory_range.second); + entry->is_valid = false; + entry->is_flushing = false; + } else { + AMS_ASSERT(entry->is_valid); + + entry->handle = this->buffer_manager->RegisterCache(memory_range.first, memory_range.second, IBufferManager::BufferAttribute(this->buffer_level)); + + entry->memory_address = 0; + entry->memory_size = 0; + entry->is_flushing = false; + } + + /* Try to succeed. */ + R_TRY(result); + + /* We succeeded. */ + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Iterate over all entries that fall within the range. */ + Result result = ResultSuccess(); + const auto max_cache_entry_count = this->GetMaxCacheEntryCount(); + for (auto i = 0; i < max_cache_entry_count; ++i) { + auto &entry = this->entries[i]; + if (entry.is_valid && (entry.is_write_back || invalidate) && (entry.offset < (offset + size)) && (offset < static_cast<s64>(entry.offset + entry.size))) { + const auto cur_result = this->FlushCacheEntry(i, invalidate); + if (R_FAILED(cur_result) && R_SUCCEEDED(result)) { + result = cur_result; + } + } + } + + /* Try to succeed. */ + R_TRY(result); + + /* We succeeded. */ + return ResultSuccess(); + } + + void BlockCacheBufferedStorage::InvalidateRangeCacheEntries(s64 offset, s64 size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->data_storage != nullptr); + AMS_ASSERT(this->buffer_manager != nullptr); + + /* Iterate over all entries that fall within the range. */ + const auto max_cache_entry_count = this->GetMaxCacheEntryCount(); + for (auto i = 0; i < max_cache_entry_count; ++i) { + auto &entry = this->entries[i]; + if (entry.is_valid && (entry.offset < (offset + size)) && (offset < static_cast<s64>(entry.offset + entry.size))) { + if (entry.is_write_back) { + AMS_ASSERT(entry.memory_address != 0 && entry.handle == 0); + this->buffer_manager->DeallocateBuffer(entry.memory_address, entry.memory_size); + } else { + AMS_ASSERT(entry.memory_address == 0 && entry.handle != 0); + const auto memory_range = this->buffer_manager->AcquireCache(entry.handle); + if (memory_range.first != 0) { + this->buffer_manager->DeallocateBuffer(memory_range.first, memory_range.second); + } + } + + /* Mark the entry as invalidated. */ + entry.is_valid = false; + entry.is_write_back = false; + entry.is_flushing = true; + } + } + } + + Result BlockCacheBufferedStorage::FlushAllCacheEntries() { + R_TRY(this->FlushRangeCacheEntries(0, std::numeric_limits<s64>::max(), false)); + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::InvalidateAllCacheEntries() { + R_TRY(this->FlushRangeCacheEntries(0, std::numeric_limits<s64>::max(), true)); + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::ControlDirtiness() { + /* Get and validate the max cache entry count. */ + const auto max_cache_entry_count = this->GetMaxCacheEntryCount(); + AMS_ASSERT(max_cache_entry_count > 0); + + /* Get size metrics from the buffer manager. */ + const auto total_size = this->buffer_manager->GetTotalSize(); + const auto allocatable_size = this->buffer_manager->GetTotalAllocatableSize(); + + /* If we have enough allocatable space, we don't need to do anything. */ + R_SUCCEED_IF(allocatable_size >= total_size / 4); + + /* Setup for flushing dirty entries. */ + auto threshold = 2; + auto dirty_count = 0; + auto flushed_index = this->invalidate_index; + + /* Iterate over all entries (starting with the invalidate index), and flush dirty entries once threshold is met. */ + for (auto i = 0; i < max_cache_entry_count; ++i) { + auto index = (this->invalidate_index + 1 + i) % max_cache_entry_count; + if (this->entries[index].is_valid && this->entries[index].is_write_back) { + ++dirty_count; + if (threshold <= dirty_count) { + R_TRY(this->FlushCacheEntry(index, false)); + flushed_index = index; + } + } + } + + /* Update the invalidate index. */ + this->invalidate_index = flushed_index; + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::UpdateLastResult(Result result) { + /* Update the last result. */ + if (R_FAILED(result) && !fs::ResultBufferAllocationFailed::Includes(result) && R_SUCCEEDED(this->last_result)) { + this->last_result = result; + } + + /* Try to succeed with the result. */ + R_TRY(result); + + /* We succeeded. */ + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::ReadHeadCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 *offset, s64 *aligned_offset, s64 aligned_offset_end, char **buffer, size_t *size) { + /* Valdiate pre-conditions. */ + AMS_ASSERT(out_range != nullptr); + AMS_ASSERT(out_entry != nullptr); + AMS_ASSERT(out_cache_needed != nullptr); + AMS_ASSERT(offset != nullptr); + AMS_ASSERT(aligned_offset != nullptr); + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(*buffer != nullptr); + AMS_ASSERT(size != nullptr); + + AMS_ASSERT(*aligned_offset < aligned_offset_end); + + /* Iterate over the region. */ + CacheEntry entry = {}; + MemoryRange memory_range = {}; + *out_cache_needed = true; + + while (*aligned_offset < aligned_offset_end) { + /* Get the associated buffer for the offset. */ + R_TRY(this->UpdateLastResult(this->GetAssociateBuffer(std::addressof(memory_range), std::addressof(entry), *aligned_offset, this->verification_block_size, true))); + + /* If the entry isn't cached, we're done. */ + if (!entry.is_cached) { + break; + } + + /* Set cache not needed. */ + *out_cache_needed = false; + + /* Determine the size to copy. */ + const s64 buffer_offset = *offset - entry.offset; + const size_t copy_size = std::min(*size, static_cast<size_t>(entry.offset + entry.size - *offset)); + + /* Copy data from the entry. */ + std::memcpy(*buffer, reinterpret_cast<const void *>(memory_range.first + buffer_offset), copy_size); + + /* Advance. */ + *buffer += copy_size; + *offset += copy_size; + *size -= copy_size; + *aligned_offset = entry.offset + entry.size; + + /* Handle the buffer. */ + R_TRY(this->UpdateLastResult(this->StoreOrDestroyBuffer(memory_range, std::addressof(entry)))); + } + + /* Set the output entry. */ + *out_entry = entry; + *out_range = memory_range; + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::ReadTailCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 offset, s64 aligned_offset, s64 *aligned_offset_end, char *buffer, size_t *size) { + /* Valdiate pre-conditions. */ + AMS_ASSERT(out_range != nullptr); + AMS_ASSERT(out_entry != nullptr); + AMS_ASSERT(out_cache_needed != nullptr); + AMS_ASSERT(aligned_offset_end != nullptr); + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(size != nullptr); + + AMS_ASSERT(aligned_offset < *aligned_offset_end); + + /* Iterate over the region. */ + CacheEntry entry = {}; + MemoryRange memory_range = {}; + *out_cache_needed = true; + + while (aligned_offset < *aligned_offset_end) { + /* Get the associated buffer for the offset. */ + R_TRY(this->UpdateLastResult(this->GetAssociateBuffer(std::addressof(memory_range), std::addressof(entry), *aligned_offset_end - this->verification_block_size, this->verification_block_size, true))); + + /* If the entry isn't cached, we're done. */ + if (!entry.is_cached) { + break; + } + + /* Set cache not needed. */ + *out_cache_needed = false; + + /* Determine the size to copy. */ + const s64 buffer_offset = std::max(static_cast<s64>(0), offset - entry.offset); + const size_t copy_size = std::min(*size, static_cast<size_t>(offset + *size - entry.offset)); + + /* Copy data from the entry. */ + std::memcpy(buffer + *size - copy_size, reinterpret_cast<const void *>(memory_range.first + buffer_offset), copy_size); + + /* Advance. */ + *size -= copy_size; + *aligned_offset_end = entry.offset; + + /* Handle the buffer. */ + R_TRY(this->UpdateLastResult(this->StoreOrDestroyBuffer(memory_range, std::addressof(entry)))); + } + + /* Set the output entry. */ + *out_entry = entry; + *out_range = memory_range; + + return ResultSuccess(); + } + + Result BlockCacheBufferedStorage::BulkRead(s64 offset, void *buffer, size_t size, MemoryRange *range_head, MemoryRange *range_tail, CacheEntry *entry_head, CacheEntry *entry_tail, bool head_cache_needed, bool tail_cache_needed) { + /* Validate pre-conditions. */ + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(range_head != nullptr); + AMS_ASSERT(range_tail != nullptr); + AMS_ASSERT(entry_head != nullptr); + AMS_ASSERT(entry_tail != nullptr); + + /* Determine bulk read offsets. */ + const s64 read_offset = offset; + const size_t read_size = size; + const s64 aligned_offset = util::AlignDown(read_offset, this->verification_block_size); + const s64 aligned_offset_end = util::AlignUp(read_offset + read_size, this->verification_block_size); + char *dst = static_cast<char *>(buffer); + + /* Prepare to do our reads. */ + auto head_guard = SCOPE_GUARD { this->DestroyBuffer(entry_head, *range_head); }; + auto tail_guard = SCOPE_GUARD { this->DestroyBuffer(entry_tail, *range_tail); }; + + /* Flush the entries. */ + R_TRY(this->UpdateLastResult(this->FlushRangeCacheEntries(aligned_offset, aligned_offset_end - aligned_offset, false))); + + /* Determine the buffer to read into. */ + PooledBuffer pooled_buffer; + const size_t buffer_size = static_cast<size_t>(aligned_offset_end - aligned_offset); + char *read_buffer = nullptr; + if (read_offset == aligned_offset && read_size == buffer_size) { + read_buffer = dst; + } else if (tail_cache_needed && entry_tail->offset == aligned_offset && entry_tail->size == buffer_size) { + read_buffer = reinterpret_cast<char *>(range_tail->first); + } else if (head_cache_needed && entry_head->offset == aligned_offset && entry_head->size == buffer_size) { + read_buffer = reinterpret_cast<char *>(range_head->first); + } else { + pooled_buffer.AllocateParticularlyLarge(buffer_size, 1); + R_UNLESS(pooled_buffer.GetSize() >= buffer_size, fs::ResultAllocationFailurePooledBufferNotEnoughSize()); + read_buffer = pooled_buffer.GetBuffer(); + } + + /* Read the data. */ + R_TRY(this->data_storage->Read(aligned_offset, read_buffer, buffer_size)); + + /* Copy the data out. */ + if (dst != read_buffer) { + std::memcpy(dst, read_buffer + read_offset - aligned_offset, read_size); + } + + /* Create a helper to populate our caches. */ + const auto PopulateCacheFromPooledBuffer = [&](CacheEntry *entry, MemoryRange *range) { + AMS_ASSERT(entry != nullptr); + AMS_ASSERT(range != nullptr); + + if (aligned_offset <= entry->offset && entry->offset + entry->size <= aligned_offset + buffer_size) { + AMS_ASSERT(!entry->is_cached); + if (reinterpret_cast<void *>(range->first) != read_buffer) { + std::memcpy(reinterpret_cast<void *>(range->first), read_buffer + entry->offset - aligned_offset, entry->size); + } + entry->is_cached = true; + } + }; + + /* Populate tail cache if needed. */ + if (tail_cache_needed) { + PopulateCacheFromPooledBuffer(entry_tail, range_tail); + } + + /* Populate head cache if needed. */ + if (head_cache_needed) { + PopulateCacheFromPooledBuffer(entry_head, range_head); + } + + /* If both entries are cached, one may contain the other; in that case, we need only the larger entry. */ + if (entry_head->is_cached && entry_tail->is_cached) { + if (entry_tail->offset <= entry_head->offset && entry_head->offset + entry_head->size <= entry_tail->offset + entry_tail->size) { + entry_head->is_cached = false; + } else if (entry_head->offset <= entry_tail->offset && entry_tail->offset + entry_tail->size <= entry_head->offset + entry_head->size) { + entry_tail->is_cached = false; + } + } + + /* Destroy the tail cache. */ + tail_guard.Cancel(); + if (entry_tail->is_cached) { + R_TRY(this->UpdateLastResult(this->StoreOrDestroyBuffer(*range_tail, entry_tail))); + } else { + this->DestroyBuffer(entry_tail, *range_tail); + } + + /* Destroy the head cache. */ + head_guard.Cancel(); + if (entry_head->is_cached) { + R_TRY(this->UpdateLastResult(this->StoreOrDestroyBuffer(*range_head, entry_head))); + } else { + this->DestroyBuffer(entry_head, *range_head); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp new file mode 100644 index 000000000..d776fdba9 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp @@ -0,0 +1,1082 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem::save { + + namespace { + + constexpr inline uintptr_t InvalidAddress = 0; + constexpr inline s64 InvalidOffset = std::numeric_limits<s64>::max(); + + } + + class BufferedStorage::Cache : public ::ams::fs::impl::Newable { + private: + struct FetchParameter { + s64 offset; + void *buffer; + size_t size; + }; + static_assert(std::is_pod<FetchParameter>::value); + private: + BufferedStorage *buffered_storage; + std::pair<uintptr_t, size_t> memory_range; + IBufferManager::CacheHandle cache_handle; + s64 offset; + std::atomic<bool> is_valid; + std::atomic<bool> is_dirty; + u8 reserved[2]; + s32 reference_count; + Cache *next; + Cache *prev; + public: + Cache() : buffered_storage(nullptr), memory_range(InvalidAddress, 0), cache_handle(), offset(InvalidOffset), is_valid(false), is_dirty(false), reference_count(1), next(nullptr), prev(nullptr) { + /* ... */ + } + + ~Cache() { + this->Finalize(); + } + + void Initialize(BufferedStorage *bs) { + AMS_ASSERT(bs != nullptr); + AMS_ASSERT(this->buffered_storage == nullptr); + + this->buffered_storage = bs; + this->Link(); + } + + void Finalize() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->reference_count == 0); + + /* If we're valid, acquire our cache handle and free our buffer. */ + if (this->IsValid()) { + const auto buffer_manager = this->buffered_storage->buffer_manager; + if (!this->is_dirty) { + AMS_ASSERT(this->memory_range.first == InvalidAddress); + this->memory_range = buffer_manager->AcquireCache(this->cache_handle); + } + if (this->memory_range.first != InvalidAddress) { + buffer_manager->DeallocateBuffer(this->memory_range.first, this->memory_range.second); + this->memory_range.first = InvalidAddress; + this->memory_range.second = 0; + } + } + + /* Clear all our members. */ + this->buffered_storage = nullptr; + this->offset = InvalidOffset; + this->is_valid = false; + this->is_dirty = false; + this->next = nullptr; + this->prev = nullptr; + } + + void Link() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->reference_count > 0); + + if ((--this->reference_count) == 0) { + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + + if (this->buffered_storage->next_fetch_cache == nullptr) { + this->buffered_storage->next_fetch_cache = this; + this->next = this; + this->prev = this; + } else { + /* Check against a cache being registered twice. */ + { + auto cache = this->buffered_storage->next_fetch_cache; + do { + if (cache->IsValid() && this->Hits(cache->offset, this->buffered_storage->block_size)) { + this->is_valid = false; + break; + } + cache = cache->next; + } while (cache != this->buffered_storage->next_fetch_cache); + } + + /* Link into the fetch list. */ + { + AMS_ASSERT(this->buffered_storage->next_fetch_cache->prev != nullptr); + AMS_ASSERT(this->buffered_storage->next_fetch_cache->prev->next == this->buffered_storage->next_fetch_cache); + this->next = this->buffered_storage->next_fetch_cache; + this->prev = this->buffered_storage->next_fetch_cache->prev; + this->next->prev = this; + this->prev->next = this; + } + + /* Insert invalid caches at the start of the list. */ + if (!this->IsValid()) { + this->buffered_storage->next_fetch_cache = this; + } + } + + /* If we're not valid, clear our offset. */ + if (!this->IsValid()) { + this->offset = InvalidOffset; + this->is_dirty = false; + } + + /* Ensure our buffer state is coherent. */ + if (this->memory_range.first != InvalidAddress && !this->is_dirty) { + if (this->IsValid()) { + this->cache_handle = this->buffered_storage->buffer_manager->RegisterCache(this->memory_range.first, this->memory_range.second, IBufferManager::BufferAttribute()); + } else { + this->buffered_storage->buffer_manager->DeallocateBuffer(this->memory_range.first, this->memory_range.second); + } + this->memory_range.first = InvalidAddress; + this->memory_range.second = 0; + } + } + } + + void Unlink() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->reference_count >= 0); + + if ((++this->reference_count) == 1) { + AMS_ASSERT(this->next != nullptr); + AMS_ASSERT(this->prev != nullptr); + AMS_ASSERT(this->next->prev == this); + AMS_ASSERT(this->prev->next == this); + + if (this->buffered_storage->next_fetch_cache == this) { + if (this->next != this) { + this->buffered_storage->next_fetch_cache = this->next; + } else { + this->buffered_storage->next_fetch_cache = nullptr; + } + } + + this->buffered_storage->next_acquire_cache = this; + + this->next->prev = this->prev; + this->prev->next = this->next; + this->next = nullptr; + this->prev = nullptr; + } else { + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + } + } + + void Read(s64 offset, void *buffer, size_t size) const { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(this->IsValid()); + AMS_ASSERT(this->Hits(offset, 1)); + AMS_ASSERT(this->memory_range.first != InvalidAddress); + + const auto read_offset = offset - this->offset; + const auto readable_offset_max = this->buffered_storage->block_size - size; + const auto cache_buffer = reinterpret_cast<u8 *>(this->memory_range.first) + read_offset; + AMS_ASSERT(read_offset >= 0); + AMS_ASSERT(static_cast<size_t>(read_offset) <= readable_offset_max); + + std::memcpy(buffer, cache_buffer, size); + } + + void Write(s64 offset, const void *buffer, size_t size) { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(this->IsValid()); + AMS_ASSERT(this->Hits(offset, 1)); + AMS_ASSERT(this->memory_range.first != InvalidAddress); + + const auto write_offset = offset - this->offset; + const auto writable_offset_max = this->buffered_storage->block_size - size; + const auto cache_buffer = reinterpret_cast<u8 *>(this->memory_range.first) + write_offset; + AMS_ASSERT(write_offset >= 0); + AMS_ASSERT(static_cast<size_t>(write_offset) <= writable_offset_max); + + std::memcpy(cache_buffer, buffer, size); + this->is_dirty = true; + } + + Result Flush() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(this->IsValid()); + + if (this->is_dirty) { + AMS_ASSERT(this->memory_range.first != InvalidAddress); + + const auto base_size = this->buffered_storage->base_storage_size; + const auto block_size = static_cast<s64>(this->buffered_storage->block_size); + const auto flush_size = static_cast<size_t>(std::min(block_size, base_size - this->offset)); + + auto &base_storage = this->buffered_storage->base_storage; + const auto cache_buffer = reinterpret_cast<void *>(this->memory_range.first); + + R_TRY(base_storage.Write(this->offset, cache_buffer, flush_size)); + this->is_dirty = false; + + buffers::EnableBlockingBufferManagerAllocation(); + } + + return ResultSuccess(); + } + + const std::pair<Result, bool> PrepareFetch() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(this->IsValid()); + AMS_ASSERT(this->buffered_storage->mutex.IsLockedByCurrentThread()); + + std::pair<Result, bool> result(ResultSuccess(), false); + if (this->reference_count == 1) { + result.first = this->Flush(); + if (R_SUCCEEDED(result.first)) { + this->is_valid = false; + this->reference_count = 0; + result.second = true; + } + } + + return result; + } + + void UnprepareFetch() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(!this->IsValid()); + AMS_ASSERT(!this->is_dirty); + AMS_ASSERT(this->buffered_storage->mutex.IsLockedByCurrentThread()); + + this->is_valid = true; + this->reference_count = 1; + } + + Result Fetch(s64 offset) { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(!this->IsValid()); + AMS_ASSERT(!this->is_dirty); + + if (this->memory_range.first == InvalidAddress) { + R_TRY(this->AllocateFetchBuffer()); + } + + FetchParameter fetch_param = {}; + this->CalcFetchParameter(std::addressof(fetch_param), offset); + + auto &base_storage = this->buffered_storage->base_storage; + R_TRY(base_storage.Read(fetch_param.offset, fetch_param.buffer, fetch_param.size)); + this->offset = fetch_param.offset; + AMS_ASSERT(this->Hits(offset, 1)); + + return ResultSuccess(); + } + + Result FetchFromBuffer(s64 offset, const void *buffer, size_t buffer_size) { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->next == nullptr); + AMS_ASSERT(this->prev == nullptr); + AMS_ASSERT(!this->IsValid()); + AMS_ASSERT(!this->is_dirty); + AMS_ASSERT(util::IsAligned(offset, this->buffered_storage->block_size)); + + if (this->memory_range.first == InvalidAddress) { + R_TRY(this->AllocateFetchBuffer()); + } + + FetchParameter fetch_param = {}; + this->CalcFetchParameter(std::addressof(fetch_param), offset); + AMS_ASSERT(fetch_param.offset == offset); + AMS_ASSERT(fetch_param.size <= buffer_size); + + std::memcpy(fetch_param.buffer, buffer, fetch_param.size); + this->offset = fetch_param.offset; + AMS_ASSERT(this->Hits(offset, 1)); + + return ResultSuccess(); + } + + bool TryAcquireCache() { + AMS_ASSERT(this->buffered_storage != nullptr); + AMS_ASSERT(this->buffered_storage->buffer_manager != nullptr); + AMS_ASSERT(this->IsValid()); + + if (this->memory_range.first != InvalidAddress) { + return true; + } else { + this->memory_range = this->buffered_storage->buffer_manager->AcquireCache(this->cache_handle); + this->is_valid = this->memory_range.first != InvalidAddress; + return this->is_valid; + } + } + + void Invalidate() { + AMS_ASSERT(this->buffered_storage != nullptr); + this->is_valid = false; + } + + bool IsValid() const { + AMS_ASSERT(this->buffered_storage != nullptr); + return this->is_valid || this->reference_count > 0; + } + + bool IsDirty() const { + AMS_ASSERT(this->buffered_storage != nullptr); + return this->is_dirty; + } + + bool Hits(s64 offset, s64 size) const { + AMS_ASSERT(this->buffered_storage != nullptr); + const auto block_size = static_cast<s64>(this->buffered_storage->block_size); + return (offset < this->offset + block_size) && (this->offset < offset + size); + } + private: + Result AllocateFetchBuffer() { + IBufferManager *buffer_manager = this->buffered_storage->buffer_manager; + AMS_ASSERT(buffer_manager->AcquireCache(this->cache_handle).first == InvalidAddress); + + auto range_guard = SCOPE_GUARD { this->memory_range.first = InvalidAddress; }; + R_TRY(buffers::AllocateBufferUsingBufferManagerContext(std::addressof(this->memory_range), buffer_manager, this->buffered_storage->block_size, IBufferManager::BufferAttribute(), [](const std::pair<uintptr_t, size_t> &buffer) { + return buffer.first != 0; + }, AMS_CURRENT_FUNCTION_NAME)); + + range_guard.Cancel(); + return ResultSuccess(); + } + + void CalcFetchParameter(FetchParameter *out, s64 offset) const { + AMS_ASSERT(out != nullptr); + + const auto block_size = static_cast<s64>(this->buffered_storage->block_size); + const auto cache_offset = util::AlignDown(offset, this->buffered_storage->block_size); + const auto base_size = this->buffered_storage->base_storage_size; + const auto cache_size = static_cast<size_t>(std::min(block_size, base_size - cache_offset)); + const auto cache_buffer = reinterpret_cast<void *>(this->memory_range.first); + AMS_ASSERT(offset >= 0); + AMS_ASSERT(offset < base_size); + + out->offset = cache_offset; + out->buffer = cache_buffer; + out->size = cache_size; + } + }; + + class BufferedStorage::SharedCache { + NON_COPYABLE(SharedCache); + NON_MOVEABLE(SharedCache); + friend class UniqueCache; + private: + Cache *cache; + Cache *start_cache; + BufferedStorage *buffered_storage; + public: + explicit SharedCache(BufferedStorage *bs) : cache(nullptr), start_cache(bs->next_acquire_cache), buffered_storage(bs) { + AMS_ASSERT(this->buffered_storage != nullptr); + } + + ~SharedCache() { + std::scoped_lock lk(buffered_storage->mutex); + this->Release(); + } + + bool AcquireNextOverlappedCache(s64 offset, s64 size) { + AMS_ASSERT(this->buffered_storage != nullptr); + + auto is_first = this->cache == nullptr; + const auto start = is_first ? this->start_cache : this->cache + 1; + + AMS_ASSERT(start >= this->buffered_storage->caches.get()); + AMS_ASSERT(start <= this->buffered_storage->caches.get() + this->buffered_storage->cache_count); + + std::scoped_lock lk(this->buffered_storage->mutex); + + this->Release(); + AMS_ASSERT(this->cache == nullptr); + + for (auto cache = start; true; ++cache) { + if (this->buffered_storage->caches.get() + this->buffered_storage->cache_count <= cache) { + cache = this->buffered_storage->caches.get(); + } + if (!is_first && cache == this->start_cache) { + break; + } + if (cache->IsValid() && cache->Hits(offset, size) && cache->TryAcquireCache()) { + cache->Unlink(); + this->cache = cache; + return true; + } + is_first = false; + } + + this->cache = nullptr; + return false; + } + + bool AcquireNextDirtyCache() { + AMS_ASSERT(this->buffered_storage != nullptr); + const auto start = this->cache != nullptr ? this->cache + 1 : this->buffered_storage->caches.get(); + const auto end = this->buffered_storage->caches.get() + this->buffered_storage->cache_count; + + AMS_ASSERT(start >= this->buffered_storage->caches.get()); + AMS_ASSERT(start <= end); + + this->Release(); + AMS_ASSERT(this->cache == nullptr); + + for (auto cache = start; cache < end; ++cache) { + if (cache->IsValid() && cache->IsDirty() && cache->TryAcquireCache()) { + cache->Unlink(); + this->cache = cache; + return true; + } + } + + this->cache = nullptr; + return false; + } + + bool AcquireNextValidCache() { + AMS_ASSERT(this->buffered_storage != nullptr); + const auto start = this->cache != nullptr ? this->cache + 1 : this->buffered_storage->caches.get(); + const auto end = this->buffered_storage->caches.get() + this->buffered_storage->cache_count; + + AMS_ASSERT(start >= this->buffered_storage->caches.get()); + AMS_ASSERT(start <= end); + + this->Release(); + AMS_ASSERT(this->cache == nullptr); + + for (auto cache = start; cache < end; ++cache) { + if (cache->IsValid() && cache->TryAcquireCache()) { + cache->Unlink(); + this->cache = cache; + return true; + } + } + + this->cache = nullptr; + return false; + } + + bool AcquireFetchableCache() { + AMS_ASSERT(this->buffered_storage != nullptr); + + std::scoped_lock lk(this->buffered_storage->mutex); + + this->Release(); + AMS_ASSERT(this->cache == nullptr); + + this->cache = this->buffered_storage->next_fetch_cache; + if (this->cache != nullptr) { + if (this->cache->IsValid()) { + this->cache->TryAcquireCache(); + } + this->cache->Unlink(); + } + + return this->cache != nullptr; + } + + void Read(s64 offset, void *buffer, size_t size) { + AMS_ASSERT(this->cache != nullptr); + this->cache->Read(offset, buffer, size); + } + + void Write(s64 offset, const void *buffer, size_t size) { + AMS_ASSERT(this->cache != nullptr); + this->cache->Write(offset, buffer, size); + } + + Result Flush() { + AMS_ASSERT(this->cache != nullptr); + return this->cache->Flush(); + } + + void Invalidate() { + AMS_ASSERT(this->cache != nullptr); + return this->cache->Invalidate(); + } + + bool Hits(s64 offset, s64 size) const { + AMS_ASSERT(this->cache != nullptr); + return this->cache->Hits(offset, size); + } + private: + void Release() { + if (this->cache != nullptr) { + AMS_ASSERT(this->buffered_storage->caches.get() <= this->cache); + AMS_ASSERT(this->cache <= this->buffered_storage->caches.get() + this->buffered_storage->cache_count); + + this->cache->Link(); + this->cache = nullptr; + } + } + }; + + class BufferedStorage::UniqueCache { + NON_COPYABLE(UniqueCache); + NON_MOVEABLE(UniqueCache); + private: + Cache *cache; + BufferedStorage *buffered_storage; + public: + explicit UniqueCache(BufferedStorage *bs) : cache(nullptr), buffered_storage(bs) { + AMS_ASSERT(this->buffered_storage != nullptr); + } + + ~UniqueCache() { + if (this->cache != nullptr) { + std::scoped_lock lk(this->buffered_storage->mutex); + this->cache->UnprepareFetch(); + } + } + + const std::pair<Result, bool> Upgrade(const SharedCache &shared_cache) { + AMS_ASSERT(this->buffered_storage == shared_cache.buffered_storage); + AMS_ASSERT(shared_cache.cache != nullptr); + + std::scoped_lock lk(this->buffered_storage->mutex); + const auto result = shared_cache.cache->PrepareFetch(); + if (R_SUCCEEDED(result.first) && result.second) { + this->cache = shared_cache.cache; + } + return result; + } + + Result Fetch(s64 offset) { + AMS_ASSERT(this->cache != nullptr); + return this->cache->Fetch(offset); + } + + Result FetchFromBuffer(s64 offset, const void *buffer, size_t buffer_size) { + AMS_ASSERT(this->cache != nullptr); + R_TRY(this->cache->FetchFromBuffer(offset, buffer, buffer_size)); + return ResultSuccess(); + } + }; + + BufferedStorage::BufferedStorage() : base_storage(), buffer_manager(), block_size(), base_storage_size(), caches(), cache_count(), next_acquire_cache(), next_fetch_cache(), mutex(false), bulk_read_enabled() { + /* ... */ + } + + BufferedStorage::~BufferedStorage() { + this->Finalize(); + } + + Result BufferedStorage::Initialize(fs::SubStorage base_storage, IBufferManager *buffer_manager, size_t block_size, s32 buffer_count) { + AMS_ASSERT(buffer_manager != nullptr); + AMS_ASSERT(block_size > 0); + AMS_ASSERT(util::IsPowerOfTwo(block_size)); + AMS_ASSERT(buffer_count > 0); + + /* Get the base storage size. */ + R_TRY(base_storage.GetSize(std::addressof(this->base_storage_size))); + + /* Set members. */ + this->base_storage = base_storage; + this->buffer_manager = buffer_manager; + this->block_size = block_size; + this->cache_count = cache_count; + + /* Allocate the caches. */ + this->caches.reset(new Cache[buffer_count]); + R_UNLESS(this->caches != nullptr, fs::ResultAllocationFailureInBufferedStorageA()); + + /* Initialize the caches. */ + for (auto i = 0; i < buffer_count; i++) { + this->caches[i].Initialize(this); + } + + this->next_acquire_cache = std::addressof(this->caches[0]); + return ResultSuccess(); + } + + void BufferedStorage::Finalize() { + this->base_storage = fs::SubStorage(); + this->base_storage_size = 0; + this->caches.reset(); + this->cache_count = 0; + this->next_fetch_cache = nullptr; + } + + Result BufferedStorage::Read(s64 offset, void *buffer, size_t size) { + AMS_ASSERT(this->IsInitialized()); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Do the read. */ + R_TRY(this->ReadCore(offset, buffer, size)); + return ResultSuccess(); + } + + Result BufferedStorage::Write(s64 offset, const void *buffer, size_t size) { + AMS_ASSERT(this->IsInitialized()); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Do the write. */ + R_TRY(this->WriteCore(offset, buffer, size)); + return ResultSuccess(); + } + + Result BufferedStorage::GetSize(s64 *out) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->IsInitialized()); + + *out = this->base_storage_size; + return ResultSuccess(); + } + + Result BufferedStorage::SetSize(s64 size) { + AMS_ASSERT(this->IsInitialized()); + const s64 prev_size = this->base_storage_size; + if (prev_size < size) { + /* Prepare to expand. */ + if (!util::IsAligned(prev_size, this->block_size)) { + SharedCache cache(this); + const auto invalidate_offset = prev_size; + const auto invalidate_size = size - prev_size; + if (cache.AcquireNextOverlappedCache(invalidate_offset, invalidate_size)) { + R_TRY(cache.Flush()); + cache.Invalidate(); + } + /* AMS_ASSERT(!cache.AcquireNextOverlappedCache(invalidate_offset, invalidate_size)); */ + } + } else if (size < prev_size) { + /* Prepare to do a shrink. */ + SharedCache cache(this); + const auto invalidate_offset = prev_size; + const auto invalidate_size = size - prev_size; + const auto is_fragment = util::IsAligned(size, this->block_size); + while (cache.AcquireNextOverlappedCache(invalidate_offset, invalidate_size)) { + if (is_fragment && cache.Hits(invalidate_offset, 1)) { + R_TRY(cache.Flush()); + } + cache.Invalidate(); + } + } + + /* Set the size. */ + R_TRY(this->base_storage.SetSize(size)); + + /* Get our new size. */ + s64 new_size = 0; + R_TRY(this->base_storage.GetSize(std::addressof(new_size))); + + this->base_storage_size = new_size; + return ResultSuccess(); + } + + Result BufferedStorage::Flush() { + AMS_ASSERT(this->IsInitialized()); + + /* Flush caches. */ + SharedCache cache(this); + while (cache.AcquireNextDirtyCache()) { + R_TRY(cache.Flush()); + } + + /* Flush the base storage. */ + R_TRY(this->base_storage.Flush()); + return ResultSuccess(); + } + + Result BufferedStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + AMS_ASSERT(this->IsInitialized()); + + /* Invalidate caches, if we should. */ + if (op_id == fs::OperationId::InvalidateCache) { + SharedCache cache(this); + while (cache.AcquireNextOverlappedCache(offset, size)) { + cache.Invalidate(); + } + } + + return this->base_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + } + + void BufferedStorage::InvalidateCaches() { + AMS_ASSERT(this->IsInitialized()); + + SharedCache cache(this); + while (cache.AcquireNextValidCache()) { + cache.Invalidate(); + } + } + + Result BufferedStorage::PrepareAllocation() { + const auto flush_threshold = this->buffer_manager->GetTotalSize() / 8; + if (this->buffer_manager->GetTotalAllocatableSize() < flush_threshold) { + R_TRY(this->Flush()); + } + return ResultSuccess(); + } + + Result BufferedStorage::ControlDirtiness() { + const auto flush_threshold = this->buffer_manager->GetTotalSize() / 4; + if (this->buffer_manager->GetTotalAllocatableSize() < flush_threshold) { + s32 dirty_count = 0; + SharedCache cache(this); + while (cache.AcquireNextDirtyCache()) { + if ((++dirty_count) > 1) { + R_TRY(cache.Flush()); + cache.Invalidate(); + } + } + } + return ResultSuccess(); + } + + Result BufferedStorage::ReadCore(s64 offset, void *buffer, size_t size) { + AMS_ASSERT(this->caches != nullptr); + AMS_ASSERT(buffer != nullptr); + + /* Validate the offset. */ + const auto base_storage_size = this->base_storage_size; + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(offset <= base_storage_size, fs::ResultInvalidOffset()); + + /* Setup tracking variables. */ + size_t remaining_size = static_cast<size_t>(std::min<s64>(size, base_storage_size - offset)); + s64 cur_offset = offset; + s64 buf_offset = 0; + + /* Determine what caches are needed, if we have bulk read set. */ + if (this->bulk_read_enabled) { + /* Check head cache. */ + const auto head_cache_needed = this->ReadHeadCache(std::addressof(cur_offset), buffer, std::addressof(remaining_size), std::addressof(buf_offset)); + R_SUCCEED_IF(remaining_size == 0); + + /* Check tail cache. */ + const auto tail_cache_needed = this->ReadTailCache(cur_offset, buffer, std::addressof(remaining_size), buf_offset); + R_SUCCEED_IF(remaining_size == 0); + + /* Perform bulk reads. */ + constexpr size_t BulkReadSizeMax = 2_MB; + if (remaining_size <= BulkReadSizeMax) { + do { + /* Try to do a bulk read. */ + R_TRY_CATCH(this->BulkRead(cur_offset, static_cast<u8 *>(buffer) + buf_offset, remaining_size, head_cache_needed, tail_cache_needed)) { + R_CATCH(fs::ResultAllocationFailurePooledBufferNotEnoughSize) { + /* If the read fails due to insufficient pooled buffer size, */ + /* then we want to fall back to the normal read path. */ + break; + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } while(0); + } + } + + /* Repeatedly read until we're done. */ + while (remaining_size > 0) { + /* Determine how much to read this iteration. */ + auto *cur_dst = static_cast<u8 *>(buffer) + buf_offset; + size_t cur_size = 0; + + if (!util::IsAligned(cur_offset, this->block_size)) { + const size_t aligned_size = this->block_size - (cur_offset & (this->block_size - 1)); + cur_size = std::min(aligned_size, remaining_size); + } else if (remaining_size < this->block_size) { + cur_size = remaining_size; + } else { + cur_size = util::AlignDown(remaining_size, this->block_size); + } + + if (cur_size <= this->block_size) { + SharedCache cache(this); + if (!cache.AcquireNextOverlappedCache(cur_offset, cur_size)) { + R_TRY(this->PrepareAllocation()); + while (true) { + R_UNLESS(cache.AcquireFetchableCache(), fs::ResultOutOfResource()); + + UniqueCache fetch_cache(this); + const auto upgrade_result = fetch_cache.Upgrade(cache); + R_TRY(upgrade_result.first); + if (upgrade_result.second) { + R_TRY(fetch_cache.Fetch(cur_offset)); + break; + } + } + R_TRY(this->ControlDirtiness()); + } + cache.Read(cur_offset, cur_dst, cur_size); + } else { + { + SharedCache cache(this); + while (cache.AcquireNextOverlappedCache(cur_offset, cur_size)) { + R_TRY(cache.Flush()); + cache.Invalidate(); + } + } + R_TRY(this->base_storage.Read(cur_offset, cur_dst, cur_size)); + } + + remaining_size -= cur_size; + cur_offset += cur_size; + buf_offset += cur_size; + } + + return ResultSuccess(); + } + + bool BufferedStorage::ReadHeadCache(s64 *offset, void *buffer, size_t *size, s64 *buffer_offset) { + AMS_ASSERT(offset != nullptr); + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(size != nullptr); + AMS_ASSERT(buffer_offset != nullptr); + + bool is_cache_needed = !util::IsAligned(*offset, this->block_size); + + while (*size > 0) { + size_t cur_size = 0; + + if (!util::IsAligned(*offset, this->block_size)) { + const s64 aligned_size = util::AlignUp(*offset, this->block_size) - *offset; + cur_size = std::min(aligned_size, static_cast<s64>(*size)); + } else if (*size < this->block_size) { + cur_size = *size; + } else { + cur_size = this->block_size; + } + + SharedCache cache(this); + if (!cache.AcquireNextOverlappedCache(*offset, cur_size)) { + break; + } + + cache.Read(*offset, static_cast<u8 *>(buffer) + *buffer_offset, cur_size); + *offset += cur_size; + *buffer_offset += cur_size; + *size -= cur_size; + is_cache_needed = false; + } + + return is_cache_needed; + } + + bool BufferedStorage::ReadTailCache(s64 offset, void *buffer, size_t *size, s64 buffer_offset) { + AMS_ASSERT(buffer != nullptr); + AMS_ASSERT(size != nullptr); + + bool is_cache_needed = !util::IsAligned(offset + *size, this->block_size); + + while (*size > 0) { + const s64 cur_offset_end = offset + *size; + size_t cur_size = 0; + + if (!util::IsAligned(offset, this->block_size)) { + const s64 aligned_size = cur_offset_end - util::AlignDown(cur_offset_end, this->block_size); + cur_size = std::min(aligned_size, static_cast<s64>(*size)); + } else if (*size < this->block_size) { + cur_size = *size; + } else { + cur_size = this->block_size; + } + + const s64 cur_offset = cur_offset_end - static_cast<s64>(cur_size); + AMS_ASSERT(cur_offset >= 0); + + SharedCache cache(this); + if (!cache.AcquireNextOverlappedCache(cur_offset, cur_size)) { + break; + } + + cache.Read(cur_offset, static_cast<u8 *>(buffer) + buffer_offset + cur_offset - offset, cur_size); + *size -= cur_size; + is_cache_needed = false; + } + + return is_cache_needed; + } + + Result BufferedStorage::BulkRead(s64 offset, void *buffer, size_t size, bool head_cache_needed, bool tail_cache_needed) { + /* Determine aligned extents. */ + const s64 aligned_offset = util::AlignDown(offset, this->block_size); + const s64 aligned_offset_end = std::min(util::AlignUp(offset + static_cast<s64>(size), this->block_size), this->base_storage_size); + const s64 aligned_size = aligned_offset_end - aligned_offset; + + /* Allocate a work buffer. */ + char *work_buffer = nullptr; + PooledBuffer pooled_buffer; + if (offset == aligned_offset && size == static_cast<size_t>(aligned_size)) { + work_buffer = static_cast<char *>(buffer); + } else { + pooled_buffer.AllocateParticularlyLarge(static_cast<size_t>(aligned_size), 1); + R_UNLESS(static_cast<s64>(pooled_buffer.GetSize()) >= aligned_size, fs::ResultAllocationFailurePooledBufferNotEnoughSize()); + work_buffer = pooled_buffer.GetBuffer(); + } + + /* Ensure cache is coherent. */ + { + SharedCache cache(this); + while (cache.AcquireNextOverlappedCache(aligned_offset, aligned_size)) { + R_TRY(cache.Flush()); + cache.Invalidate(); + } + } + + /* Read from the base storage. */ + R_TRY(this->base_storage.Read(aligned_offset, work_buffer, static_cast<size_t>(aligned_size))); + if (work_buffer != static_cast<char *>(buffer)) { + std::memcpy(buffer, work_buffer + offset - aligned_offset, size); + } + + bool cached = false; + + /* Handle head cache if needed. */ + if (head_cache_needed) { + R_TRY(this->PrepareAllocation()); + + SharedCache cache(this); + while (true) { + R_UNLESS(cache.AcquireFetchableCache(), fs::ResultOutOfResource()); + + UniqueCache fetch_cache(this); + const auto upgrade_result = fetch_cache.Upgrade(cache); + R_TRY(upgrade_result.first); + if (upgrade_result.second) { + R_TRY(fetch_cache.FetchFromBuffer(aligned_offset, work_buffer, static_cast<size_t>(aligned_size))); + break; + } + } + + cached = true; + } + + /* Handle tail cache if needed. */ + if (tail_cache_needed && (!head_cache_needed || aligned_size > static_cast<s64>(this->block_size))) { + if (!cached) { + R_TRY(this->PrepareAllocation()); + } + + SharedCache cache(this); + while (true) { + R_UNLESS(cache.AcquireFetchableCache(), fs::ResultOutOfResource()); + + UniqueCache fetch_cache(this); + const auto upgrade_result = fetch_cache.Upgrade(cache); + R_TRY(upgrade_result.first); + if (upgrade_result.second) { + const s64 tail_cache_offset = util::AlignDown(offset + static_cast<s64>(size), this->block_size); + const size_t tail_cache_size = static_cast<size_t>(aligned_size - tail_cache_offset + aligned_offset); + R_TRY(fetch_cache.FetchFromBuffer(tail_cache_offset, work_buffer + tail_cache_offset - aligned_offset, tail_cache_size)); + break; + } + } + } + + if (cached) { + R_TRY(this->ControlDirtiness()); + } + + return ResultSuccess(); + } + + Result BufferedStorage::WriteCore(s64 offset, const void *buffer, size_t size) { + AMS_ASSERT(this->caches != nullptr); + AMS_ASSERT(buffer != nullptr); + + /* Validate the offset. */ + const auto base_storage_size = this->base_storage_size; + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(offset <= base_storage_size, fs::ResultInvalidOffset()); + + /* Setup tracking variables. */ + size_t remaining_size = static_cast<size_t>(std::min<s64>(size, base_storage_size - offset)); + s64 cur_offset = offset; + s64 buf_offset = 0; + + /* Repeatedly read until we're done. */ + while (remaining_size > 0) { + /* Determine how much to read this iteration. */ + const auto *cur_src = static_cast<const u8 *>(buffer) + buf_offset; + size_t cur_size = 0; + + if (!util::IsAligned(cur_offset, this->block_size)) { + const size_t aligned_size = this->block_size - (cur_offset & (this->block_size - 1)); + cur_size = std::min(aligned_size, remaining_size); + } else if (remaining_size < this->block_size) { + cur_size = remaining_size; + } else { + cur_size = util::AlignDown(remaining_size, this->block_size); + } + + if (cur_size <= this->block_size) { + SharedCache cache(this); + if (!cache.AcquireNextOverlappedCache(cur_offset, cur_size)) { + R_TRY(this->PrepareAllocation()); + while (true) { + R_UNLESS(cache.AcquireFetchableCache(), fs::ResultOutOfResource()); + + UniqueCache fetch_cache(this); + const auto upgrade_result = fetch_cache.Upgrade(cache); + R_TRY(upgrade_result.first); + if (upgrade_result.second) { + R_TRY(fetch_cache.Fetch(cur_offset)); + break; + } + } + } + cache.Write(cur_offset, cur_src, cur_size); + + buffers::EnableBlockingBufferManagerAllocation(); + + R_TRY(this->ControlDirtiness()); + } else { + { + SharedCache cache(this); + while (cache.AcquireNextOverlappedCache(cur_offset, cur_size)) { + R_TRY(cache.Flush()); + cache.Invalidate(); + } + } + + R_TRY(this->base_storage.Write(cur_offset, cur_src, cur_size)); + + buffers::EnableBlockingBufferManagerAllocation(); + } + + remaining_size -= cur_size; + cur_offset += cur_size; + buf_offset += cur_size; + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp new file mode 100644 index 000000000..d4dbb1c4b --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp @@ -0,0 +1,352 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem::save { + + namespace { + + constexpr inline u32 IntegrityVerificationStorageMagic = util::FourCC<'I','V','F','C'>::Code; + constexpr inline u32 IntegrityVerificationStorageVersion = 0x00020000; + constexpr inline u32 IntegrityVerificationStorageVersionMask = 0xFFFF0000; + + constexpr inline auto MaxSaveDataFsDataCacheEntryCount = 32; + constexpr inline auto MaxSaveDataFsHashCacheEntryCount = 4; + constexpr inline auto MaxRomFsDataCacheEntryCount = 24; + constexpr inline auto MaxRomFsHashCacheEntryCount = 8; + + constexpr inline auto AccessCountMax = 5; + constexpr inline auto AccessTimeout = TimeSpan::FromMilliSeconds(10); + + os::Semaphore g_read_semaphore(AccessCountMax, AccessCountMax); + os::Semaphore g_write_semaphore(AccessCountMax, AccessCountMax); + + constexpr inline const char MasterKey[] = "HierarchicalIntegrityVerificationStorage::Master"; + constexpr inline const char L1Key[] = "HierarchicalIntegrityVerificationStorage::L1"; + constexpr inline const char L2Key[] = "HierarchicalIntegrityVerificationStorage::L2"; + constexpr inline const char L3Key[] = "HierarchicalIntegrityVerificationStorage::L3"; + constexpr inline const char L4Key[] = "HierarchicalIntegrityVerificationStorage::L4"; + constexpr inline const char L5Key[] = "HierarchicalIntegrityVerificationStorage::L5"; + + constexpr inline const struct { + const char *key; + size_t size; + } KeyArray[] = { + { MasterKey, sizeof(MasterKey) }, + { L1Key, sizeof(L1Key) }, + { L2Key, sizeof(L2Key) }, + { L3Key, sizeof(L3Key) }, + { L4Key, sizeof(L4Key) }, + { L5Key, sizeof(L5Key) }, + }; + + } + + /* Instantiate the global random generation function. */ + HierarchicalIntegrityVerificationStorage::GenerateRandomFunction HierarchicalIntegrityVerificationStorage::s_generate_random = nullptr; + + Result HierarchicalIntegrityVerificationStorageControlArea::QuerySize(HierarchicalIntegrityVerificationSizeSet *out, const InputParam &input_param, s32 layer_count, s64 data_size) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT((static_cast<s32>(IntegrityMinLayerCount) <= layer_count) && (layer_count <= static_cast<s32>(IntegrityMaxLayerCount))); + for (s32 level = 0; level < (layer_count - 1); ++level) { + AMS_ASSERT(input_param.level_block_size[level] > 0); + AMS_ASSERT(IsPowerOfTwo(static_cast<s32>(input_param.level_block_size[level]))); + } + + /* Set the control size. */ + out->control_size = sizeof(HierarchicalIntegrityVerificationMetaInformation); + + /* Determine the level sizes. */ + s64 level_size[IntegrityMaxLayerCount]; + s32 level = layer_count - 1; + + level_size[level] = util::AlignUp(data_size, input_param.level_block_size[level - 1]); + --level; + + for (/* ... */; level > 0; --level) { + level_size[level] = util::AlignUp(level_size[level + 1] / input_param.level_block_size[level] * HashSize, input_param.level_block_size[level - 1]); + } + + /* Determine the master size. */ + level_size[0] = level_size[1] / input_param.level_block_size[0] * HashSize; + + /* Set the master size. */ + out->master_hash_size = level_size[0]; + + /* Set the level sizes. */ + for (level = 1; level < layer_count - 1; ++level) { + out->layered_hash_sizes[level - 1] = level_size[level]; + } + + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorageControlArea::Expand(fs::SubStorage meta_storage, const HierarchicalIntegrityVerificationMetaInformation &meta) { + /* Check the meta size. */ + { + s64 meta_size = 0; + R_TRY(meta_storage.GetSize(std::addressof(meta_size))); + R_UNLESS(meta_size >= static_cast<s64>(sizeof(meta)), fs::ResultInvalidSize()); + } + + /* Validate both the previous and new metas. */ + { + /* Read the previous meta. */ + HierarchicalIntegrityVerificationMetaInformation prev_meta = {}; + R_TRY(meta_storage.Read(0, std::addressof(prev_meta), sizeof(prev_meta))); + + /* Validate both magics. */ + R_UNLESS(prev_meta.magic == IntegrityVerificationStorageMagic, fs::ResultIncorrectIntegrityVerificationMagic()); + R_UNLESS(prev_meta.magic == meta.magic, fs::ResultIncorrectIntegrityVerificationMagic()); + + /* Validate both versions. */ + R_UNLESS(prev_meta.version == IntegrityVerificationStorageVersion, fs::ResultUnsupportedVersion()); + R_UNLESS(prev_meta.version == meta.version, fs::ResultUnsupportedVersion()); + } + + /* Write the new meta. */ + R_TRY(meta_storage.Write(0, std::addressof(meta), sizeof(meta))); + R_TRY(meta_storage.Flush()); + + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorageControlArea::Initialize(fs::SubStorage meta_storage) { + /* Check the meta size. */ + { + s64 meta_size = 0; + R_TRY(meta_storage.GetSize(std::addressof(meta_size))); + R_UNLESS(meta_size >= static_cast<s64>(sizeof(this->meta)), fs::ResultInvalidSize()); + } + + /* Set the storage and read the meta. */ + this->storage = meta_storage; + R_TRY(this->storage.Read(0, std::addressof(this->meta), sizeof(this->meta))); + + /* Validate the meta magic. */ + R_UNLESS(this->meta.magic == IntegrityVerificationStorageMagic, fs::ResultIncorrectIntegrityVerificationMagic()); + + /* Validate the meta version. */ + R_UNLESS((this->meta.version & IntegrityVerificationStorageVersionMask) == (IntegrityVerificationStorageVersion & IntegrityVerificationStorageVersionMask), fs::ResultUnsupportedVersion()); + + return ResultSuccess(); + } + + void HierarchicalIntegrityVerificationStorageControlArea::Finalize() { + this->storage = fs::SubStorage(); + } + + Result HierarchicalIntegrityVerificationStorage::Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, os::Mutex *mtx, fs::StorageType storage_type) { + /* Validate preconditions. */ + AMS_ASSERT(bufs != nullptr); + AMS_ASSERT(IntegrityMinLayerCount <= info.max_layers && info.max_layers <= IntegrityMaxLayerCount); + + /* Set member variables. */ + this->max_layers = info.max_layers; + this->buffers = bufs; + this->mutex = mtx; + + /* Determine our cache counts. */ + const auto max_data_cache_entry_count = (storage_type == fs::StorageType_SaveData) ? MaxSaveDataFsDataCacheEntryCount : MaxRomFsDataCacheEntryCount; + const auto max_hash_cache_entry_count = (storage_type == fs::StorageType_SaveData) ? MaxSaveDataFsHashCacheEntryCount : MaxRomFsHashCacheEntryCount; + + /* Initialize the top level verification storage. */ + { + fs::HashSalt mac; + crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[0].key, KeyArray[0].size); + this->verify_storages[0].Initialize(storage[HierarchicalStorageInformation::MasterStorage], storage[HierarchicalStorageInformation::Layer1Storage], static_cast<s64>(1) << info.info[0].block_order, HashSize, this->buffers->buffers[this->max_layers - 2], mac, false, storage_type); + } + + /* Ensure we don't leak state if further initialization goes wrong. */ + auto top_verif_guard = SCOPE_GUARD { + this->verify_storages[0].Finalize(); + + this->data_size = -1; + this->buffers = nullptr; + this->mutex = nullptr; + }; + + /* Initialize the top level buffer storage. */ + R_TRY(this->buffer_storages[0].Initialize(this->buffers->buffers[0], this->mutex, std::addressof(this->verify_storages[0]), info.info[0].size, static_cast<s64>(1) << info.info[0].block_order, max_hash_cache_entry_count, false, 0x10, false, storage_type)); + auto top_buffer_guard = SCOPE_GUARD { this->buffer_storages[0].Finalize(); }; + + /* Prepare to initialize the level storages. */ + s32 level = 0; + + /* Ensure we don't leak state if further initialization goes wrong. */ + auto level_guard = SCOPE_GUARD { + this->verify_storages[level + 1].Finalize(); + for (/* ... */; level > 0; --level) { + this->buffer_storages[level].Finalize(); + this->verify_storages[level].Finalize(); + } + }; + + /* Initialize the level storages. */ + for (/* ... */; level < this->max_layers - 3; ++level) { + /* Initialize the verification storage. */ + { + fs::SubStorage buffer_storage(std::addressof(this->buffer_storages[level]), 0, info.info[level].size); + fs::HashSalt mac; + crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size); + this->verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, this->buffers->buffers[this->max_layers - 2], mac, false, storage_type); + } + + /* Initialize the buffer storage. */ + R_TRY(this->buffer_storages[level + 1].Initialize(this->buffers->buffers[level + 1], this->mutex, std::addressof(this->verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_hash_cache_entry_count, false, 0x11 + static_cast<s8>(level), false, storage_type)); + } + + /* Initialize the final level storage. */ + { + /* Initialize the verification storage. */ + { + fs::SubStorage buffer_storage(std::addressof(this->buffer_storages[level]), 0, info.info[level].size); + fs::HashSalt mac; + crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size); + this->verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, this->buffers->buffers[this->max_layers - 2], mac, true, storage_type); + } + + /* Initialize the buffer storage. */ + R_TRY(this->buffer_storages[level + 1].Initialize(this->buffers->buffers[level + 1], this->mutex, std::addressof(this->verify_storages[level + 1]), info.info[level + 1].size, static_cast<s64>(1) << info.info[level + 1].block_order, max_data_cache_entry_count, true, 0x11 + static_cast<s8>(level), true, storage_type)); + } + + /* Set the data size. */ + this->data_size = info.info[level + 1].size; + + /* We succeeded. */ + level_guard.Cancel(); + top_buffer_guard.Cancel(); + top_verif_guard.Cancel(); + return ResultSuccess(); + } + + void HierarchicalIntegrityVerificationStorage::Finalize() { + if (this->data_size >= 0) { + this->data_size = 0; + + this->buffers = nullptr; + this->mutex = nullptr; + + for (s32 level = this->max_layers - 2; level >= 0; --level) { + this->buffer_storages[level].Finalize(); + this->verify_storages[level].Finalize(); + } + + this->data_size = -1; + } + } + + Result HierarchicalIntegrityVerificationStorage::Read(s64 offset, void *buffer, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(this->data_size >= 0); + + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Acquire access to the read semaphore. */ + if (!g_read_semaphore.TimedAcquire(AccessTimeout)) { + for (auto level = this->max_layers - 2; level >= 0; --level) { + R_TRY(this->buffer_storages[level].Flush()); + } + g_read_semaphore.Acquire(); + } + + /* Ensure that we release the semaphore when done. */ + ON_SCOPE_EXIT { g_read_semaphore.Release(); }; + + /* Read the data. */ + R_TRY(this->buffer_storages[this->max_layers - 2].Read(offset, buffer, size)); + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorage::Write(s64 offset, const void *buffer, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(this->data_size >= 0); + + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Acquire access to the write semaphore. */ + if (!g_write_semaphore.TimedAcquire(AccessTimeout)) { + for (auto level = this->max_layers - 2; level >= 0; --level) { + R_TRY(this->buffer_storages[level].Flush()); + } + g_write_semaphore.Acquire(); + } + + /* Ensure that we release the semaphore when done. */ + ON_SCOPE_EXIT { g_write_semaphore.Release(); }; + + /* Write the data. */ + R_TRY(this->buffer_storages[this->max_layers - 2].Write(offset, buffer, size)); + this->is_written_for_rollback = true; + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorage::GetSize(s64 *out) { + AMS_ASSERT(out != nullptr); + AMS_ASSERT(this->data_size >= 0); + *out = this->data_size; + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorage::Flush() { + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + switch (op_id) { + case fs::OperationId::Clear: + case fs::OperationId::ClearSignature: + { + R_TRY(this->buffer_storages[this->max_layers - 2].OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + this->is_written_for_rollback = true; + return ResultSuccess(); + } + case fs::OperationId::InvalidateCache: + case fs::OperationId::QueryRange: + { + R_TRY(this->buffer_storages[this->max_layers - 2].OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInHierarchicalIntegrityVerificationStorageB(); + } + } + + Result HierarchicalIntegrityVerificationStorage::Commit() { + for (s32 level = this->max_layers - 2; level >= 0; --level) { + R_TRY(this->buffer_storages[level].Commit()); + } + return ResultSuccess(); + } + + Result HierarchicalIntegrityVerificationStorage::OnRollback() { + for (s32 level = this->max_layers - 2; level >= 0; --level) { + R_TRY(this->buffer_storages[level].OnRollback()); + } + this->is_written_for_rollback = false; + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp new file mode 100644 index 000000000..c2f5737b0 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp @@ -0,0 +1,484 @@ +/* + * 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 <stratosphere.hpp> + +namespace ams::fssystem::save { + + Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type) { + /* Validate preconditions. */ + AMS_ASSERT(verif_block_size >= HashSize); + AMS_ASSERT(bm != nullptr); + + /* Set storages. */ + this->hash_storage = hs; + this->data_storage = ds; + + /* Set verification block sizes. */ + this->verification_block_size = verif_block_size; + this->verification_block_order = ILog2(static_cast<u32>(verif_block_size)); + AMS_ASSERT(this->verification_block_size == (1l << this->verification_block_order)); + + /* Set buffer manager. */ + this->buffer_manager = bm; + + /* Set upper layer block sizes. */ + upper_layer_verif_block_size = std::max(upper_layer_verif_block_size, HashSize); + this->upper_layer_verification_block_size = upper_layer_verif_block_size; + this->upper_layer_verification_block_order = ILog2(static_cast<u32>(upper_layer_verif_block_size)); + AMS_ASSERT(this->upper_layer_verification_block_size == (1l << this->upper_layer_verification_block_order)); + + /* Validate sizes. */ + { + s64 hash_size = 0; + s64 data_size = 0; + AMS_ASSERT(R_SUCCEEDED(hash_storage.GetSize(std::addressof(hash_size)))); + AMS_ASSERT(R_SUCCEEDED(data_storage.GetSize(std::addressof(hash_size)))); + AMS_ASSERT(((hash_size / HashSize) * this->verification_block_size) >= data_size); + } + + /* Set salt. */ + std::memcpy(this->salt.value, salt.value, fs::HashSalt::Size); + + /* Set data and storage type. */ + this->is_real_data = is_real_data; + this->storage_type = storage_type; + return ResultSuccess(); + } + + void IntegrityVerificationStorage::Finalize() { + if (this->buffer_manager != nullptr) { + this->hash_storage = fs::SubStorage(); + this->data_storage = fs::SubStorage(); + this->buffer_manager = nullptr; + } + } + + Result IntegrityVerificationStorage::Read(s64 offset, void *buffer, size_t size) { + /* Although we support zero-size reads, we expect non-zero sizes. */ + AMS_ASSERT(size != 0); + + /* Validate other preconditions. */ + AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(this->verification_block_size))); + AMS_ASSERT(util::IsAligned(size, static_cast<size_t>(this->verification_block_size))); + + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + + /* Validate the offset. */ + s64 data_size; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(offset <= data_size, fs::ResultInvalidOffset()); + + /* Validate the access range. */ + R_UNLESS(IStorage::IsRangeValid(offset, size, util::AlignUp(data_size, static_cast<size_t>(this->verification_block_size))), fs::ResultOutOfRange()); + + /* Determine the read extents. */ + size_t read_size = size; + if (static_cast<s64>(offset + read_size) > data_size) { + /* Determine the padding sizes. */ + s64 padding_offset = data_size - offset; + size_t padding_size = static_cast<size_t>(this->verification_block_size - (padding_offset & (this->verification_block_size - 1))); + AMS_ASSERT(static_cast<s64>(padding_size) < this->verification_block_size); + + /* Clear the padding. */ + std::memset(static_cast<u8 *>(buffer) + padding_offset, 0, padding_size); + + /* Set the new in-bounds size. */ + read_size = static_cast<size_t>(data_size - offset); + } + + /* Perform the read. */ + { + auto clear_guard = SCOPE_GUARD { std::memset(buffer, 0, size); }; + R_TRY(this->data_storage.Read(offset, buffer, read_size)); + clear_guard.Cancel(); + } + + /* Prepare to validate the signatures. */ + const auto signature_count = size >> this->verification_block_order; + PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash)); + const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / sizeof(BlockHash)); + + /* Verify the signatures. */ + Result verify_hash_result = ResultSuccess(); + + size_t verified_count = 0; + while (verified_count < signature_count) { + /* Read the current signatures. */ + const auto cur_count = std::min(buffer_count, signature_count - verified_count); + auto cur_result = this->ReadBlockSignature(signature_buffer.GetBuffer(), signature_buffer.GetSize(), offset + (verified_count << this->verification_block_order), cur_count << this->verification_block_order); + + /* Temporarily increase our priority. */ + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + + /* Loop over each signature we read. */ + for (size_t i = 0; i < cur_count && R_SUCCEEDED(cur_result); ++i) { + const auto verified_size = (verified_count + i) << this->verification_block_order; + u8 *cur_buf = static_cast<u8 *>(buffer) + verified_size; + cur_result = this->VerifyHash(cur_buf, reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i); + + /* If the data is corrupted, clear the corrupted parts. */ + if (fs::ResultIntegrityVerificationStorageCorrupted::Includes(cur_result)) { + std::memset(cur_buf, 0, this->verification_block_size); + + /* Set the result if we should. */ + if (!fs::ResultClearedRealDataVerificationFailed::Includes(cur_result) && this->storage_type != fs::StorageType_Authoring) { + verify_hash_result = cur_result; + } + + cur_result = ResultSuccess(); + } + } + + /* If we failed, clear and return. */ + if (R_FAILED(cur_result)) { + std::memset(buffer, 0, size); + return cur_result; + } + + /* Advance. */ + verified_count += cur_count; + } + + return verify_hash_result; + } + + Result IntegrityVerificationStorage::Write(s64 offset, const void *buffer, size_t size) { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); + + /* Validate arguments. */ + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultInvalidOffset()); + + /* Validate the offset. */ + s64 data_size; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(offset < data_size, fs::ResultInvalidOffset()); + + /* Validate the access range. */ + R_UNLESS(IStorage::IsRangeValid(offset, size, util::AlignUp(data_size, static_cast<size_t>(this->verification_block_size))), fs::ResultOutOfRange()); + + /* Validate preconditions. */ + AMS_ASSERT(util::IsAligned(offset, this->verification_block_size)); + AMS_ASSERT(util::IsAligned(size, this->verification_block_size)); + AMS_ASSERT(offset <= data_size); + AMS_ASSERT(static_cast<s64>(offset + size) < data_size + this->verification_block_size); + + /* Validate that if writing past the end, all extra data is zero padding. */ + if (static_cast<s64>(offset + size) > data_size) { + const u8 *padding_cur = static_cast<const u8 *>(buffer) + data_size - offset; + const u8 *padding_end = padding_cur + (offset + size - data_size); + + while (padding_cur < padding_end) { + AMS_ASSERT((*padding_cur) == 0); + ++padding_cur; + } + } + + /* Determine the unpadded size to write. */ + auto write_size = size; + if (static_cast<s64>(offset + write_size) > data_size) { + write_size = static_cast<size_t>(data_size - offset); + R_SUCCEED_IF(write_size == 0); + } + + /* Determine the size we're writing in blocks. */ + const auto aligned_write_size = util::AlignUp(write_size, this->verification_block_size); + + /* Write the updated block signatures. */ + Result update_result = ResultSuccess(); + size_t updated_count = 0; + { + const auto signature_count = aligned_write_size >> this->verification_block_order; + PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash)); + const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / sizeof(BlockHash)); + + while (updated_count < signature_count) { + const auto cur_count = std::min(buffer_count, signature_count - updated_count); + + /* Calculate the hash with temporarily increased priority. */ + { + ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); + + for (size_t i = 0; i < cur_count; ++i) { + const auto updated_size = (updated_count + i) << this->verification_block_order; + this->CalcBlockHash(reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i, reinterpret_cast<const u8 *>(buffer) + updated_size); + } + } + + /* Write the new block signatures. */ + if (R_FAILED((update_result = this->WriteBlockSignature(signature_buffer.GetBuffer(), signature_buffer.GetSize(), offset + (updated_count << this->verification_block_order), cur_count << this->verification_block_order)))) { + break; + } + + /* Advance. */ + updated_count += cur_count; + } + } + + /* Write the data. */ + R_TRY(this->data_storage.Write(offset, buffer, std::min(write_size, updated_count << this->verification_block_order))); + + return update_result; + } + + Result IntegrityVerificationStorage::GetSize(s64 *out) { + return this->data_storage.GetSize(out); + } + + Result IntegrityVerificationStorage::Flush() { + /* Flush both storages. */ + R_TRY(this->hash_storage.Flush()); + R_TRY(this->data_storage.Flush()); + return ResultSuccess(); + } + + Result IntegrityVerificationStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + /* Validate preconditions. */ + AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(this->verification_block_size))); + AMS_ASSERT(util::IsAligned(size, static_cast<size_t>(this->verification_block_size))); + + switch (op_id) { + case fs::OperationId::Clear: + { + /* Clear should only be called for save data. */ + AMS_ASSERT(this->storage_type == fs::StorageType_SaveData); + + /* Validate the range. */ + s64 data_size = 0; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(0 <= offset && offset <= data_size, fs::ResultInvalidOffset()); + + /* Determine the extents to clear. */ + const auto sign_offset = (offset >> this->verification_block_order) * HashSize; + const auto sign_size = (std::min(size, data_size - offset) >> this->verification_block_order) * HashSize; + + /* Allocate a work buffer. */ + const auto buf_size = static_cast<size_t>(std::min(sign_size, static_cast<s64>(1) << (this->upper_layer_verification_block_order + 2))); + std::unique_ptr<char[], fs::impl::Deleter> buf = fs::impl::MakeUnique<char[]>(buf_size); + R_UNLESS(buf != nullptr, fs::ResultAllocationFailureInIntegrityVerificationStorageA()); + + /* Clear the work buffer. */ + std::memset(buf.get(), 0, buf_size); + + /* Clear in chunks. */ + auto remaining_size = sign_size; + + while (remaining_size > 0) { + const auto cur_size = static_cast<size_t>(std::min(remaining_size, static_cast<s64>(buf_size))); + R_TRY(this->hash_storage.Write(sign_offset + sign_size - remaining_size, buf.get(), cur_size)); + remaining_size -= cur_size; + } + + return ResultSuccess(); + } + case fs::OperationId::ClearSignature: + { + /* Clear Signature should only be called for save data. */ + AMS_ASSERT(this->storage_type == fs::StorageType_SaveData); + + /* Validate the range. */ + s64 data_size = 0; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(0 <= offset && offset <= data_size, fs::ResultInvalidOffset()); + + /* Determine the extents to clear the signature for. */ + const auto sign_offset = (offset >> this->verification_block_order) * HashSize; + const auto sign_size = (std::min(size, data_size - offset) >> this->verification_block_order) * HashSize; + + /* Allocate a work buffer. */ + std::unique_ptr<char[], fs::impl::Deleter> buf = fs::impl::MakeUnique<char[]>(sign_size); + R_UNLESS(buf != nullptr, fs::ResultAllocationFailureInIntegrityVerificationStorageB()); + + /* Read the existing signature. */ + R_TRY(this->hash_storage.Read(sign_offset, buf.get(), sign_size)); + + /* Clear the signature. */ + /* This sets all bytes to FF, with the verification bit cleared. */ + for (auto i = 0; i < sign_size; ++i) { + buf[i] ^= ((i + 1) % HashSize == 0 ? 0x7F : 0xFF); + } + + /* Write the cleared signature. */ + return this->hash_storage.Write(sign_offset, buf.get(), sign_size); + } + case fs::OperationId::InvalidateCache: + { + /* Only allow cache invalidation for RomFs. */ + R_UNLESS(this->storage_type != fs::StorageType_SaveData, fs::ResultUnsupportedOperationInIntegrityVerificationStorageB()); + + /* Validate the range. */ + s64 data_size = 0; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(0 <= offset && offset <= data_size, fs::ResultInvalidOffset()); + + /* Determine the extents to invalidate. */ + const auto sign_offset = (offset >> this->verification_block_order) * HashSize; + const auto sign_size = (std::min(size, data_size - offset) >> this->verification_block_order) * HashSize; + + /* Operate on our storages. */ + R_TRY(this->hash_storage.OperateRange(dst, dst_size, op_id, sign_offset, sign_size, src, src_size)); + R_TRY(this->data_storage.OperateRange(dst, dst_size, op_id, sign_offset, sign_size, src, src_size)); + + return ResultSuccess(); + } + case fs::OperationId::QueryRange: + { + /* Validate the range. */ + s64 data_size = 0; + R_TRY(this->data_storage.GetSize(std::addressof(data_size))); + R_UNLESS(0 <= offset && offset <= data_size, fs::ResultInvalidOffset()); + + /* Determine the real size to query. */ + const auto actual_size = std::min(size, data_size - offset); + + /* Query the data storage. */ + R_TRY(this->data_storage.OperateRange(dst, dst_size, op_id, offset, actual_size, src, src_size)); + + return ResultSuccess(); + } + default: + return fs::ResultUnsupportedOperationInIntegrityVerificationStorageC(); + } + } + + void IntegrityVerificationStorage::CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const { + /* Create a sha256 generator. */ + crypto::Sha256Generator sha; + sha.Initialize(); + + /* If calculating for save data, hash the salt. */ + if (this->storage_type == fs::StorageType_SaveData) { + sha.Update(this->salt.value, sizeof(this->salt)); + } + + /* Update with the buffer and get the hash. */ + sha.Update(buffer, block_size); + sha.GetHash(out, sizeof(*out)); + + /* Set the validation bit, if the hash is for save data. */ + if (this->storage_type == fs::StorageType_SaveData) { + SetValidationBit(out); + } + } + + Result IntegrityVerificationStorage::ReadBlockSignature(void *dst, size_t dst_size, s64 offset, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(this->verification_block_size))); + AMS_ASSERT(util::IsAligned(size, static_cast<size_t>(this->verification_block_size))); + + /* Determine where to read the signature. */ + const s64 sign_offset = (offset >> this->verification_block_order) * HashSize; + const auto sign_size = static_cast<size_t>((size >> this->verification_block_order) * HashSize); + AMS_ASSERT(dst_size >= sign_size); + + /* Create a guard in the event of failure. */ + auto clear_guard = SCOPE_GUARD { std::memset(dst, 0, sign_size); }; + + /* Validate that we can read the signature. */ + s64 hash_size; + R_TRY(this->hash_storage.GetSize(std::addressof(hash_size))); + const bool range_valid = static_cast<s64>(sign_offset + sign_size) <= hash_size; + AMS_ASSERT(range_valid); + R_UNLESS(range_valid, fs::ResultOutOfRange()); + + /* Read the signature. */ + R_TRY(this->hash_storage.Read(sign_offset, dst, sign_size)); + + /* We succeeded. */ + clear_guard.Cancel(); + return ResultSuccess(); + } + + Result IntegrityVerificationStorage::WriteBlockSignature(const void *src, size_t src_size, s64 offset, size_t size) { + /* Validate preconditions. */ + AMS_ASSERT(src != nullptr); + AMS_ASSERT(util::IsAligned(offset, static_cast<size_t>(this->verification_block_size))); + + /* Determine where to write the signature. */ + const s64 sign_offset = (offset >> this->verification_block_order) * HashSize; + const auto sign_size = static_cast<size_t>((size >> this->verification_block_order) * HashSize); + AMS_ASSERT(src_size >= sign_size); + + /* Write the signature. */ + R_TRY(this->hash_storage.Write(sign_offset, src, sign_size)); + + /* We succeeded. */ + return ResultSuccess(); + } + + Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash) { + /* Validate preconditions. */ + AMS_ASSERT(buf != nullptr); + AMS_ASSERT(hash != nullptr); + + /* Get the comparison hash. */ + auto &cmp_hash = *hash; + + /* If save data, check if the data is uninitialized. */ + if (this->storage_type == fs::StorageType_SaveData) { + bool is_cleared = false; + R_TRY(this->IsCleared(std::addressof(is_cleared), cmp_hash)); + R_UNLESS(!is_cleared, fs::ResultClearedRealDataVerificationFailed()); + } + + /* Get the calculated hash. */ + BlockHash calc_hash; + this->CalcBlockHash(std::addressof(calc_hash), buf); + + /* Check that the signatures are equal. */ + if (!crypto::IsSameBytes(std::addressof(cmp_hash), std::addressof(calc_hash), sizeof(BlockHash))) { + /* Clear the comparison hash. */ + std::memset(std::addressof(cmp_hash), 0, sizeof(cmp_hash)); + + /* Return the appropriate result. */ + if (this->is_real_data) { + return fs::ResultUnclearedRealDataVerificationFailed(); + } else { + return fs::ResultNonRealDataVerificationFailed(); + } + } + + return ResultSuccess(); + } + + Result IntegrityVerificationStorage::IsCleared(bool *is_cleared, const BlockHash &hash) { + /* Validate preconditions. */ + AMS_ASSERT(is_cleared != nullptr); + AMS_ASSERT(this->storage_type == fs::StorageType_SaveData); + + /* Default to uncleared. */ + *is_cleared = false; + + /* Succeed if the validation bit is set. */ + R_SUCCEED_IF(IsValidationBit(std::addressof(hash))); + + /* Otherwise, we expect the hash to be all zero. */ + for (size_t i = 0; i < sizeof(hash.hash); ++i) { + R_UNLESS(hash.hash[i] == 0, fs::ResultInvalidZeroHash()); + } + + /* Set cleared. */ + *is_cleared = true; + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp b/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp index f0a4a029c..092211ffb 100644 --- a/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp @@ -32,4 +32,12 @@ namespace ams::hos { hos::SetVersionForLibnxInternal(); } + void InitializeForStratosphereDebug(hos::Version debug_version) { + /* Initialize the global os resource managers. This *must* be done before anything else in stratosphere. */ + os::InitializeForStratosphereInternal(); + + /* Initialize hos::Version API. */ + hos::SetVersionForLibnxInternalDebug(debug_version); + } + } diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index 56f4b65b8..2d31bbbe6 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -49,6 +49,14 @@ namespace ams::hos { return g_hos_version; } + + void SetVersionForLibnxInternalDebug(hos::Version debug_version) { + std::scoped_lock lk(g_mutex); + g_hos_version = debug_version; + __atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST); + SetVersionForLibnxInternal(); + } + void SetVersionForLibnxInternal() { const u32 hos_version_val = static_cast<u32>(hos::GetVersion()); const u32 major = (hos_version_val >> 24) & 0xFF; diff --git a/libraries/libstratosphere/source/hos/hos_version_api_private.hpp b/libraries/libstratosphere/source/hos/hos_version_api_private.hpp index ed0cb8953..80f1c6fcd 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api_private.hpp +++ b/libraries/libstratosphere/source/hos/hos_version_api_private.hpp @@ -19,5 +19,6 @@ namespace ams::hos { void SetVersionForLibnxInternal(); + void SetVersionForLibnxInternalDebug(hos::Version debug_version); } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp index 53beb516e..59c3cd507 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_storage_impl.cpp @@ -627,7 +627,7 @@ namespace ams::ncm { R_TRY(this->EnsureEnabled()); /* This command is for development hardware only. */ - AMS_ABORT_UNLESS(spl::IsDevelopmentHardware()); + AMS_ABORT_UNLESS(spl::IsDevelopment()); /* Close any cached file. */ this->InvalidateFileCache(); diff --git a/libraries/libstratosphere/source/spl/spl_api.cpp b/libraries/libstratosphere/source/spl/spl_api.cpp index 7ffa4c333..6e8240244 100644 --- a/libraries/libstratosphere/source/spl/spl_api.cpp +++ b/libraries/libstratosphere/source/spl/spl_api.cpp @@ -17,17 +17,204 @@ namespace ams::spl { - HardwareType GetHardwareType() { - u64 out_val = 0; - R_ABORT_UNLESS(splGetConfig(SplConfigItem_HardwareType, &out_val)); - return static_cast<HardwareType>(out_val); + namespace { + + enum class InitializeMode { + None, + General, + Crypto, + Ssl, + Es, + Fs, + Manu + }; + + os::Mutex g_mutex(false); + s32 g_initialize_count = 0; + InitializeMode g_initialize_mode = InitializeMode::None; + + Result AllocateAesKeySlotImpl(s32 *out) { + return serviceDispatchOut(splCryptoGetServiceSession(), 21, *out); + } + + Result DeallocateAesKeySlotImpl(s32 slot) { + return serviceDispatchIn(splCryptoGetServiceSession(), 22, slot); + } + + Result GetAesKeySlotAvailableEventImpl(Handle *out) { + return serviceDispatch(splCryptoGetServiceSession(), 23, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out, + ); + } + + void GetAesKeySlotAvailableEvent(os::SystemEvent *out) { + /* Get libnx event. */ + Handle handle = svc::InvalidHandle; + R_ABORT_UNLESS(GetAesKeySlotAvailableEventImpl(std::addressof(handle))); + + /* Attach to event. */ + out->AttachReadableHandle(handle, true, os::EventClearMode_ManualClear); + } + + template<typename F> + Result WaitAvailableKeySlotAndExecute(F f) { + os::SystemEvent event; + auto is_event_initialized = false; + while (true) { + R_TRY_CATCH(static_cast<::ams::Result>(f())) { + R_CATCH(spl::ResultOutOfKeyslots) { + if (!is_event_initialized) { + GetAesKeySlotAvailableEvent(std::addressof(event)); + is_event_initialized = true; + } + event.Wait(); + continue; + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + } + + template<typename F> + void Initialize(InitializeMode mode, F f) { + std::scoped_lock lk(g_mutex); + + AMS_ASSERT(g_initialize_count >= 0); + AMS_ABORT_UNLESS(mode != InitializeMode::None); + + if (g_initialize_count == 0) { + AMS_ABORT_UNLESS(g_initialize_mode == InitializeMode::None); + f(); + g_initialize_mode = mode; + } else { + AMS_ABORT_UNLESS(g_initialize_mode == mode); + } + + ++g_initialize_count; + } + + } + + void Initialize() { + return Initialize(InitializeMode::General, [&]() { + R_ABORT_UNLESS(splInitialize()); + }); + } + + void InitializeForCrypto() { + return Initialize(InitializeMode::Crypto, [&]() { + R_ABORT_UNLESS(splCryptoInitialize()); + }); + } + + void InitializeForSsl() { + return Initialize(InitializeMode::Ssl, [&]() { + R_ABORT_UNLESS(splSslInitialize()); + }); + } + + void InitializeForEs() { + return Initialize(InitializeMode::Es, [&]() { + R_ABORT_UNLESS(splEsInitialize()); + }); + } + + void InitializeForFs() { + return Initialize(InitializeMode::Fs, [&]() { + R_ABORT_UNLESS(splFsInitialize()); + }); + } + + void InitializeForManu() { + return Initialize(InitializeMode::Manu, [&]() { + R_ABORT_UNLESS(splManuInitialize()); + }); + } + + void Finalize() { + std::scoped_lock lk(g_mutex); + AMS_ASSERT(g_initialize_count > 0); + AMS_ABORT_UNLESS(g_initialize_mode != InitializeMode::None); + + if ((--g_initialize_count) == 0) { + switch (g_initialize_mode) { + case InitializeMode::General: splExit(); break; + case InitializeMode::Crypto: splCryptoExit(); break; + case InitializeMode::Ssl: splSslExit(); break; + case InitializeMode::Es: splEsExit(); break; + case InitializeMode::Fs: splFsExit(); break; + case InitializeMode::Manu: splManuExit(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + g_initialize_mode = InitializeMode::None; + } + } + + Result AllocateAesKeySlot(s32 *out_slot) { + return WaitAvailableKeySlotAndExecute([&]() -> Result { + return AllocateAesKeySlotImpl(out_slot); + }); + } + + Result DeallocateAesKeySlot(s32 slot) { + return DeallocateAesKeySlotImpl(slot); + } + + Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, s32 generation, u32 option) { + AMS_ASSERT(key_source_size == sizeof(KeySource)); + return splCryptoGenerateAesKek(key_source, generation, option, static_cast<void *>(access_key)); + } + + Result LoadAesKey(s32 slot, const AccessKey &access_key, const void *key_source, size_t key_source_size) { + AMS_ASSERT(key_source_size == sizeof(KeySource)); + return splCryptoLoadAesKey(std::addressof(access_key), key_source, static_cast<u32>(slot)); + } + + Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size) { + AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize); + AMS_ASSERT(key_source_size == sizeof(KeySource)); + return WaitAvailableKeySlotAndExecute([&]() -> Result { + return splCryptoGenerateAesKey(std::addressof(access_key), key_source, dst); + }); + } + + Result GenerateSpecificAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option) { + AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize); + AMS_ASSERT(key_source_size == sizeof(KeySource)); + return splFsGenerateSpecificAesKey(key_source, static_cast<u32>(generation), option, dst); + } + + Result ComputeCtr(void *dst, size_t dst_size, s32 slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + AMS_ASSERT(iv_size >= 0x10); + AMS_ASSERT(dst_size >= src_size); + + return splCryptoCryptAesCtr(src, dst, src_size, static_cast<s32>(slot), iv); + } + + Result DecryptAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option) { + AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize); + AMS_ASSERT(key_source_size == sizeof(KeySource)); + return WaitAvailableKeySlotAndExecute([&]() -> Result { + return splCryptoDecryptAesKey(key_source, static_cast<u32>(generation), option, dst); + }); + } + + Result GetConfig(u64 *out, ConfigItem item) { + return splGetConfig(static_cast<::SplConfigItem>(item), out); + } + + bool IsDevelopment() { + bool is_dev; + R_ABORT_UNLESS(splIsDevelopment(std::addressof(is_dev))); + return is_dev; } MemoryArrangement GetMemoryArrangement() { - u64 arrange = 0; - R_ABORT_UNLESS(splGetConfig(SplConfigItem_MemoryArrange, &arrange)); - arrange &= 0x3F; - switch (arrange) { + u64 mode = 0; + R_ABORT_UNLESS(spl::GetConfig(std::addressof(mode), spl::ConfigItem::MemoryMode)); + switch (mode & 0x3F) { case 2: return MemoryArrangement_StandardForAppletDev; case 3: @@ -41,52 +228,55 @@ namespace ams::spl { } } - bool IsDisabledProgramVerification() { - u64 val = 0; - R_ABORT_UNLESS(splGetConfig(SplConfigItem_DisableProgramVerification, &val)); - return val != 0; + Result SetBootReason(BootReasonValue boot_reason) { + static_assert(sizeof(boot_reason) == sizeof(u32)); + + u32 v; + std::memcpy(std::addressof(v), std::addressof(boot_reason), sizeof(v)); + + return splSetBootReason(v); } - bool IsDevelopmentHardware() { - bool is_dev_hardware; - R_ABORT_UNLESS(splIsDevelopment(&is_dev_hardware)); - return is_dev_hardware; + Result GetBootReason(BootReasonValue *out) { + static_assert(sizeof(*out) == sizeof(u32)); + + u32 v; + R_TRY(splGetBootReason(std::addressof(v))); + + std::memcpy(out, std::addressof(v), sizeof(*out)); + return ResultSuccess(); } - bool IsDevelopmentFunctionEnabled() { - u64 val = 0; - R_ABORT_UNLESS(splGetConfig(SplConfigItem_IsDebugMode, &val)); - return val != 0; - } - - bool IsRecoveryBoot() { - u64 val = 0; - R_ABORT_UNLESS(splGetConfig(SplConfigItem_IsRecoveryBoot, &val)); - return val != 0; - } - - bool IsMariko() { - const auto hw_type = GetHardwareType(); - switch (hw_type) { + SocType GetSocType() { + switch (GetHardwareType()) { case HardwareType::Icosa: case HardwareType::Copper: - return false; + return SocType_Erista; case HardwareType::Hoag: case HardwareType::Iowa: - return true; + case HardwareType::_Five_: + return SocType_Mariko; AMS_UNREACHABLE_DEFAULT_CASE(); } } - Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, u32 generation, u32 option) { - AMS_ASSERT(key_source_size == sizeof(KeySource)); - return splCryptoGenerateAesKek(key_source, generation, option, static_cast<void *>(access_key)); + Result GetPackage2Hash(void *dst, size_t dst_size) { + AMS_ASSERT(dst_size >= crypto::Sha256Generator::HashSize); + return splFsGetPackage2Hash(dst); } - Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size) { - AMS_ASSERT(dst_size == crypto::AesEncryptor128::KeySize); - AMS_ASSERT(key_source_size == sizeof(KeySource)); - return splCryptoGenerateAesKey(std::addressof(access_key), key_source, dst); + Result GenerateRandomBytes(void *out, size_t buffer_size) { + return splGetRandomBytes(out, buffer_size); + } + + Result LoadPreparedAesKey(s32 slot, const AccessKey &access_key) { + if (g_initialize_mode == InitializeMode::Fs) { + return splFsLoadTitlekey(std::addressof(access_key), static_cast<u32>(slot)); + } else { + /* TODO: libnx binding not available. */ + /* return splEsLoadTitlekey(std::addressof(access_key), static_cast<u32>(slot)); */ + AMS_ABORT_UNLESS(false); + } } } diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp index 6fd9c0487..2aa531f67 100644 --- a/libraries/libvapours/include/vapours/crypto.hpp +++ b/libraries/libvapours/include/vapours/crypto.hpp @@ -29,4 +29,6 @@ #include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp> +#include <vapours/crypto/crypto_hmac_sha1_generator.hpp> +#include <vapours/crypto/crypto_hmac_sha256_generator.hpp> #include <vapours/crypto/crypto_csrng.hpp> diff --git a/libraries/libvapours/include/vapours/crypto/crypto_hmac_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_hmac_generator.hpp new file mode 100644 index 000000000..f1e8cefc7 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_hmac_generator.hpp @@ -0,0 +1,51 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util.hpp> +#include <vapours/crypto/impl/crypto_hmac_impl.hpp> + +namespace ams::crypto { + + template<typename Hash> /* requires HashFunction<Hash> */ + class HmacGenerator { + NON_COPYABLE(HmacGenerator); + NON_MOVEABLE(HmacGenerator); + private: + using Impl = impl::HmacImpl<Hash>; + public: + static constexpr size_t HashSize = Impl::HashSize; + static constexpr size_t BlockSize = Impl::BlockSize; + private: + Impl impl; + public: + HmacGenerator() { /* ... */ } + + void Initialize(const void *key, size_t key_size) { + return this->impl.Initialize(key, key_size); + } + + void Update(const void *data, size_t size) { + return this->impl.Update(data, size); + } + + void GetMac(void *dst, size_t dst_size) { + return this->impl.GetMac(dst, dst_size); + } + }; +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha1_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha1_generator.hpp new file mode 100644 index 000000000..44faf7dcf --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha1_generator.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/crypto/crypto_sha1_generator.hpp> +#include <vapours/crypto/crypto_hmac_generator.hpp> + +namespace ams::crypto { + + using HmacSha1Generator = HmacGenerator<Sha1Generator>; + + void GenerateHmacSha1Mac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size); + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha256_generator.hpp b/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha256_generator.hpp new file mode 100644 index 000000000..8d4f24a85 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_hmac_sha256_generator.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/crypto/crypto_sha256_generator.hpp> +#include <vapours/crypto/crypto_hmac_generator.hpp> + +namespace ams::crypto { + + using HmacSha256Generator = HmacGenerator<Sha256Generator>; + + void GenerateHmacSha256Mac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size); + +} diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_hmac_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_hmac_impl.hpp new file mode 100644 index 000000000..961506b40 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_hmac_impl.hpp @@ -0,0 +1,126 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util.hpp> +#include <vapours/crypto/impl/crypto_hash_function.hpp> +#include <vapours/crypto/crypto_memory_clear.hpp> + +namespace ams::crypto::impl { + + template<typename Hash> /* requires HashFunction<Hash> */ + class HmacImpl { + NON_COPYABLE(HmacImpl); + NON_MOVEABLE(HmacImpl); + public: + static constexpr size_t MacSize = Hash::HashSize; + static constexpr size_t BlockSize = Hash::BlockSize; + private: + static constexpr u32 IpadMagic = 0x36363636; + static constexpr u32 OpadMagic = 0x5c5c5c5c; + + static constexpr u32 IpadMagicXorOpadMagic = IpadMagic ^ OpadMagic; + static_assert(IpadMagicXorOpadMagic == 0x6a6a6a6a); + private: + enum State { + State_None = 0, + State_Initialized = 1, + State_Done = 2, + }; + private: + Hash hash_function; + u32 key[BlockSize / sizeof(u32)]; + u32 mac[MacSize / sizeof(u32)]; + State state; + public: + HmacImpl() : state(State_None) { /* ... */ } + ~HmacImpl() { + static_assert(offsetof(HmacImpl, hash_function) == 0); + + /* Clear everything except for the hash function. */ + ClearMemory(reinterpret_cast<u8 *>(this) + sizeof(this->hash_function), sizeof(*this) - sizeof(this->hash_function)); + } + + void Initialize(const void *key, size_t key_size); + void Update(const void *data, size_t data_size); + void GetMac(void *dst, size_t dst_size); + }; + + template<typename Hash> + inline void HmacImpl<Hash>::Initialize(const void *key, size_t key_size) { + /* Clear the key storage. */ + std::memset(this->key, 0, sizeof(this->key)); + + /* Set the key storage. */ + if (key_size > BlockSize) { + this->hash_function.Initialize(); + this->hash_function.Update(key, key_size); + this->hash_function.GetHash(this->key, this->hash_function.HashSize); + } else { + std::memcpy(this->key, key, key_size); + } + + /* Xor the key with the ipad. */ + for (size_t i = 0; i < util::size(this->key); i++) { + this->key[i] ^= IpadMagic; + } + + /* Update the hash function with the xor'd key. */ + this->hash_function.Initialize(); + this->hash_function.Update(this->key, BlockSize); + + /* Mark initialized. */ + this->state = State_Initialized; + } + + template<typename Hash> + inline void HmacImpl<Hash>::Update(const void *data, size_t data_size) { + AMS_ASSERT(this->state == State_Initialized); + + this->hash_function.Update(data, data_size); + } + + template<typename Hash> + inline void HmacImpl<Hash>::GetMac(void *dst, size_t dst_size) { + AMS_ASSERT(this->state == State_Initialized || this->state == State_Done); + AMS_ASSERT(dst_size >= MacSize); + + /* If we're not already finalized, get the final mac. */ + if (this->state == State_Initialized) { + /* Get the hash of ((key ^ ipad) || data). */ + this->hash_function.GetHash(this->mac, MacSize); + + /* Xor the key with the opad. */ + for (size_t i = 0; i < util::size(this->key); i++) { + this->key[i] ^= IpadMagicXorOpadMagic; + } + + /* Calculate the final mac as hash of ((key ^ opad) || hash((key ^ ipad) || data)) */ + this->hash_function.Initialize(); + this->hash_function.Update(this->key, BlockSize); + this->hash_function.Update(this->mac, MacSize); + this->hash_function.GetHash(this->mac, MacSize); + + /* Set our state as done. */ + this->state = State_Done; + } + + std::memcpy(dst, this->mac, MacSize); + } + +} diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index 5d5ae5eee..c4e9962ec 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -35,9 +35,12 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(NotEnoughFreeSpaceBisSystem, 38); R_DEFINE_ERROR_RESULT(NotEnoughFreeSpaceSdCard, 39); + R_DEFINE_ERROR_RESULT(UnsupportedSdkVersion, 50); + R_DEFINE_ERROR_RESULT(MountNameAlreadyExists, 60); - R_DEFINE_ERROR_RESULT(TargetNotFound, 1002); + R_DEFINE_ERROR_RESULT(PartitionNotFound, 1001); + R_DEFINE_ERROR_RESULT(TargetNotFound, 1002); R_DEFINE_ERROR_RANGE(SdCardAccessFailed, 2000, 2499); R_DEFINE_ERROR_RESULT(SdCardNotPresent, 2001); @@ -51,48 +54,65 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(SystemPartitionNotReady, 3100); R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); - R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211); - R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212); - R_DEFINE_ERROR_RESULT(AllocationFailureInApplicationA, 3213); - R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215); - R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216); - R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217); - R_DEFINE_ERROR_RESULT(AllocationFailureInCodeA, 3218); - R_DEFINE_ERROR_RESULT(AllocationFailureInContentA, 3219); - R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220); - R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221); - R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222); - R_DEFINE_ERROR_RESULT(AllocationFailureInDataB, 3223); - R_DEFINE_ERROR_RESULT(AllocationFailureInDeviceSaveDataA, 3224); - R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225); - R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226); - R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227); - R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228); - R_DEFINE_ERROR_RESULT(AllocationFailureInImageDirectoryA, 3232); - R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244); - R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245); - R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246); - R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247); - R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248); - R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280); - R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemBuddyHeapA, 3294); - R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemA, 3347); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemB, 3348); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemC, 3349); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaA, 3350); - R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaB, 3351); - R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352); - R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); - R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365); - R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366); - R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); - R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375); - R_DEFINE_ERROR_RESULT(AllocationFailureInReadOnlyFileSystemA, 3386); - R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377); - R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); - R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212); + R_DEFINE_ERROR_RESULT(AllocationFailureInApplicationA, 3213); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216); + R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217); + R_DEFINE_ERROR_RESULT(AllocationFailureInCodeA, 3218); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentA, 3219); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageA, 3220); + R_DEFINE_ERROR_RESULT(AllocationFailureInContentStorageB, 3221); + R_DEFINE_ERROR_RESULT(AllocationFailureInDataA, 3222); + R_DEFINE_ERROR_RESULT(AllocationFailureInDataB, 3223); + R_DEFINE_ERROR_RESULT(AllocationFailureInDeviceSaveDataA, 3224); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardA, 3225); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardB, 3226); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardC, 3227); + R_DEFINE_ERROR_RESULT(AllocationFailureInGameCardD, 3228); + R_DEFINE_ERROR_RESULT(AllocationFailureInImageDirectoryA, 3232); + R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardA, 3244); + R_DEFINE_ERROR_RESULT(AllocationFailureInSdCardB, 3245); + R_DEFINE_ERROR_RESULT(AllocationFailureInSystemSaveDataA, 3246); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFileSystemCreatorA, 3281); + R_DEFINE_ERROR_RESULT(AllocationFailureInStorageOnNcaCreatorA, 3288); + R_DEFINE_ERROR_RESULT(AllocationFailureInStorageOnNcaCreatorB, 3289); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemBuddyHeapA, 3294); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemBufferManagerA, 3295); + R_DEFINE_ERROR_RESULT(AllocationFailureInBlockCacheBufferedStorageA, 3296); + R_DEFINE_ERROR_RESULT(AllocationFailureInBlockCacheBufferedStorageB, 3297); + R_DEFINE_ERROR_RESULT(AllocationFailureInIntegrityVerificationStorageA, 3304); + R_DEFINE_ERROR_RESULT(AllocationFailureInIntegrityVerificationStorageB, 3305); + R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); + R_DEFINE_ERROR_RESULT(AllocationFailureInNcaFileSystemDriverI, 3341); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemA, 3347); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemB, 3348); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemC, 3349); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaA, 3350); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaB, 3351); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352); + R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); + R_DEFINE_ERROR_RESULT(AllocationFailureInNcaReaderA, 3363); + R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365); + R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366); + R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); + R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375); + R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377); + R_DEFINE_ERROR_RESULT(AllocationFailureInReadOnlyFileSystemA, 3386); + R_DEFINE_ERROR_RESULT(AllocationFailureInAesCtrCounterExtendedStorageA, 3399); + R_DEFINE_ERROR_RESULT(AllocationFailureInAesCtrCounterExtendedStorageB, 3400); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); + R_DEFINE_ERROR_RESULT(AllocationFailureInBufferedStorageA, 3411); + R_DEFINE_ERROR_RESULT(AllocationFailureInIntegrityRomFsStorageA, 3412); + R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420); + R_DEFINE_ERROR_RESULT(AllocationFailureInMakeUnique, 3422); + R_DEFINE_ERROR_RESULT(AllocationFailureInAllocateShared, 3423); + R_DEFINE_ERROR_RESULT(AllocationFailurePooledBufferNotEnoughSize, 3424); R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999); @@ -100,6 +120,28 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(RomCorrupted, 4001, 4299); R_DEFINE_ERROR_RESULT(UnsupportedRomVersion, 4002); + R_DEFINE_ERROR_RANGE(AesCtrCounterExtendedStorageCorrupted, 4011, 4019); + R_DEFINE_ERROR_RESULT(InvalidAesCtrCounterExtendedEntryOffset, 4012); + R_DEFINE_ERROR_RESULT(InvalidAesCtrCounterExtendedTableSize, 4013); + R_DEFINE_ERROR_RESULT(InvalidAesCtrCounterExtendedGeneration, 4014); + R_DEFINE_ERROR_RESULT(InvalidAesCtrCounterExtendedOffset, 4015); + + R_DEFINE_ERROR_RANGE(IndirectStorageCorrupted, 4021, 4029); + R_DEFINE_ERROR_RESULT(InvalidIndirectEntryOffset, 4022); + R_DEFINE_ERROR_RESULT(InvalidIndirectEntryStorageIndex, 4023); + R_DEFINE_ERROR_RESULT(InvalidIndirectStorageSize, 4024); + R_DEFINE_ERROR_RESULT(InvalidIndirectVirtualOffset, 4025); + R_DEFINE_ERROR_RESULT(InvalidIndirectPhysicalOffset, 4026); + R_DEFINE_ERROR_RESULT(InvalidIndirectStorageIndex, 4027); + + R_DEFINE_ERROR_RANGE(BucketTreeCorrupted, 4031, 4039); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeSignature, 4032); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeEntryCount, 4033); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeNodeEntryCount, 4034); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeNodeOffset, 4035); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeEntryOffset, 4036); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeEntrySetOffset, 4037); + R_DEFINE_ERROR_RANGE(RomNcaCorrupted, 4041, 4139); R_DEFINE_ERROR_RANGE(RomNcaFileSystemCorrupted, 4051, 4069); R_DEFINE_ERROR_RESULT(InvalidRomNcaFileSystemType, 4052); @@ -154,6 +196,9 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(SaveDataCorrupted, 4301, 4499); R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599); + R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeA, 4508); + R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeB, 4509); + R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4511, 4529); R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType, 4512); R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513); @@ -167,6 +212,12 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(InvalidNcaKeyIndex, 4521); R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderHashType, 4522); R_DEFINE_ERROR_RESULT(InvalidNcaFsHeaderEncryptionType, 4523); + R_DEFINE_ERROR_RESULT(InvalidNcaPatchInfoIndirectSize, 4524); + R_DEFINE_ERROR_RESULT(InvalidNcaPatchInfoAesCtrExSize, 4525); + R_DEFINE_ERROR_RESULT(InvalidNcaPatchInfoAesCtrExOffset, 4526); + R_DEFINE_ERROR_RESULT(InvalidNcaId, 4527); + R_DEFINE_ERROR_RESULT(InvalidNcaHeader, 4528); + R_DEFINE_ERROR_RESULT(InvalidNcaFsHeader, 4529); R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539); R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532); @@ -174,6 +225,9 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(HierarchicalSha256BaseStorageTooLarge, 4534); R_DEFINE_ERROR_RESULT(HierarchicalSha256HashVerificationFailed, 4535); + /* TODO: Range? */ + R_DEFINE_ERROR_RESULT(InvalidNcaHeader1SignatureKeyGeneration, 4543); + R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639); R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602); R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603); @@ -254,28 +308,51 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(WriteNotPermitted, 6203); R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageB, 6307); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrStorageA, 6315); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateA, 6369); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateB, 6370); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemA, 6374); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageB, 6307); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSwitchStorageA, 6308); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrCounterExtendedStorageA, 6310); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrCounterExtendedStorageB, 6311); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrCounterExtendedStorageC, 6312); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrStorageExternalA, 6313); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrStorageExternalB, 6314); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInAesCtrStorageA, 6315); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInHierarchicalIntegrityVerificationStorageA, 6316); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInHierarchicalIntegrityVerificationStorageB, 6317); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIntegrityVerificationStorageA, 6318); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIntegrityVerificationStorageB, 6319); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIntegrityVerificationStorageC, 6320); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInBlockCacheBufferedStorageA, 6321); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInBlockCacheBufferedStorageB, 6322); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInBlockCacheBufferedStorageC, 6323); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIndirectStorageA, 6324); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIndirectStorageB, 6325); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIndirectStorageC, 6326); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInZeroStorageA, 6327); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInZeroStorageB, 6328); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInHierarchicalSha256StorageA, 6329); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyBlockCacheStorageA, 6330); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyBlockCacheStorageB, 6331); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInIntegrityRomFsStorageA , 6332); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateA, 6369); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateB, 6370); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemA, 6374); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449); diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 867668f83..43e2806e1 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -36,3 +36,4 @@ #include <vapours/util/util_uuid.hpp> #include <vapours/util/util_bounded_map.hpp> #include <vapours/util/util_string_util.hpp> +#include <vapours/util/util_variadic.hpp> diff --git a/libraries/libvapours/include/vapours/util/util_bitutil.hpp b/libraries/libvapours/include/vapours/util/util_bitutil.hpp index cf974f62d..bc52b45f4 100644 --- a/libraries/libvapours/include/vapours/util/util_bitutil.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitutil.hpp @@ -208,4 +208,11 @@ namespace ams::util { return T(1) << (BITSIZEOF(T) - CountLeadingZeros(x) - 1); } + template<typename T, typename U> + constexpr ALWAYS_INLINE T DivideUp(T v, U d) { + using Unsigned = typename std::make_unsigned<U>::type; + const Unsigned add = static_cast<Unsigned>(d) - 1; + return static_cast<T>((v + add) / d); + } + } diff --git a/libraries/libvapours/include/vapours/util/util_variadic.hpp b/libraries/libvapours/include/vapours/util/util_variadic.hpp new file mode 100644 index 000000000..91ed72e0d --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_variadic.hpp @@ -0,0 +1,84 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> + +#define AMS_UTIL_VARIADIC_INVOKE_MACRO(__HANDLER__) \ + __HANDLER__(_01_) \ + __HANDLER__(_02_) \ + __HANDLER__(_03_) \ + __HANDLER__(_04_) \ + __HANDLER__(_05_) \ + __HANDLER__(_06_) \ + __HANDLER__(_07_) \ + __HANDLER__(_08_) \ + __HANDLER__(_09_) \ + __HANDLER__(_0A_) \ + __HANDLER__(_0B_) \ + __HANDLER__(_0C_) \ + __HANDLER__(_0D_) \ + __HANDLER__(_0E_) \ + __HANDLER__(_0F_) + +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_01_(_T_) typename _T_##_01_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_02_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_01_(_T_), typename _T_##_02_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_03_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_02_(_T_), typename _T_##_03_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_04_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_03_(_T_), typename _T_##_04_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_05_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_04_(_T_), typename _T_##_05_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_06_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_05_(_T_), typename _T_##_06_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_07_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_06_(_T_), typename _T_##_07_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_08_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_07_(_T_), typename _T_##_08_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_09_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_08_(_T_), typename _T_##_09_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0A_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_09_(_T_), typename _T_##_0A_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0B_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0A_(_T_), typename _T_##_0B_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0C_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0B_(_T_), typename _T_##_0C_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0D_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0C_(_T_), typename _T_##_0D_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0E_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0D_(_T_), typename _T_##_0E_ +#define AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0F_(_T_) AMS_UTIL_VARIADIC_TEMPLATE_PARAMETERS_0E_(_T_), typename _T_##_0F_ + +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_01_(_T_, _N_) _T_##_01_ &&_N_##_01_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_02_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_01_(_T_, _N_), _T_##_02_ &&_N_##_02_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_03_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_02_(_T_, _N_), _T_##_03_ &&_N_##_03_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_04_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_03_(_T_, _N_), _T_##_04_ &&_N_##_04_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_05_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_04_(_T_, _N_), _T_##_05_ &&_N_##_05_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_06_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_05_(_T_, _N_), _T_##_06_ &&_N_##_06_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_07_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_06_(_T_, _N_), _T_##_07_ &&_N_##_07_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_08_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_07_(_T_, _N_), _T_##_08_ &&_N_##_08_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_09_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_08_(_T_, _N_), _T_##_09_ &&_N_##_09_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0A_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_09_(_T_, _N_), _T_##_0A_ &&_N_##_0A_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0B_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0A_(_T_, _N_), _T_##_0B_ &&_N_##_0B_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0C_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0B_(_T_, _N_), _T_##_0C_ &&_N_##_0C_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0D_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0C_(_T_, _N_), _T_##_0D_ &&_N_##_0D_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0E_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0D_(_T_, _N_), _T_##_0E_ &&_N_##_0E_ +#define AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0F_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS_0E_(_T_, _N_), _T_##_0F_ &&_N_##_0F_ + +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_01_(_T_, _N_) ::std::forward<_T_##_01_>(_N_##_01_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_02_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_01_(_T_, _N_), ::std::forward<_T_##_02_>(_N_##_02_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_03_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_02_(_T_, _N_), ::std::forward<_T_##_03_>(_N_##_03_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_04_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_03_(_T_, _N_), ::std::forward<_T_##_04_>(_N_##_04_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_05_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_04_(_T_, _N_), ::std::forward<_T_##_05_>(_N_##_05_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_06_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_05_(_T_, _N_), ::std::forward<_T_##_06_>(_N_##_06_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_07_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_06_(_T_, _N_), ::std::forward<_T_##_07_>(_N_##_07_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_08_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_07_(_T_, _N_), ::std::forward<_T_##_08_>(_N_##_08_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_09_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_08_(_T_, _N_), ::std::forward<_T_##_09_>(_N_##_09_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0A_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_09_(_T_, _N_), ::std::forward<_T_##_0A_>(_N_##_0A_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0B_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0A_(_T_, _N_), ::std::forward<_T_##_0B_>(_N_##_0B_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0C_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0B_(_T_, _N_), ::std::forward<_T_##_0C_>(_N_##_0C_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0D_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0C_(_T_, _N_), ::std::forward<_T_##_0D_>(_N_##_0D_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0E_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0D_(_T_, _N_), ::std::forward<_T_##_0E_>(_N_##_0E_) +#define AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0F_(_T_, _N_) AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS_0E_(_T_, _N_), ::std::forward<_T_##_0F_>(_N_##_0F_) diff --git a/libraries/libvapours/source/crypto/crypto_hmac_sha1_generator.cpp b/libraries/libvapours/source/crypto/crypto_hmac_sha1_generator.cpp new file mode 100644 index 000000000..d7a7ab2a5 --- /dev/null +++ b/libraries/libvapours/source/crypto/crypto_hmac_sha1_generator.cpp @@ -0,0 +1,28 @@ +/* + * 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 <vapours.hpp> + +namespace ams::crypto { + + void GenerateHmacSha1Mac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size) { + HmacSha1Generator hmac; + + hmac.Initialize(key, key_size); + hmac.Update(data, data_size); + hmac.GetMac(dst, dst_size); + } + +} diff --git a/libraries/libvapours/source/crypto/crypto_hmac_sha256_generator.cpp b/libraries/libvapours/source/crypto/crypto_hmac_sha256_generator.cpp new file mode 100644 index 000000000..52b36e42f --- /dev/null +++ b/libraries/libvapours/source/crypto/crypto_hmac_sha256_generator.cpp @@ -0,0 +1,28 @@ +/* + * 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 <vapours.hpp> + +namespace ams::crypto { + + void GenerateHmacSha256Mac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size) { + HmacSha256Generator hmac; + + hmac.Initialize(key, key_size); + hmac.Update(data, data_size); + hmac.GetMac(dst, dst_size); + } + +} diff --git a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp index f1c2cc8fc..96f89c68a 100644 --- a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp @@ -96,7 +96,7 @@ namespace ams::mitm { { u64 key_generation = 0; if (hos::GetVersion() >= hos::Version_5_0_0) { - R_ABORT_UNLESS(splGetConfig(SplConfigItem_NewKeyGeneration, &key_generation)); + R_ABORT_UNLESS(spl::GetConfig(std::addressof(key_generation), spl::ConfigItem::DeviceUniqueKeyGeneration)); } u8 bis_keys[4][2][0x10]; @@ -107,15 +107,15 @@ namespace ams::mitm { for (size_t partition = 0; partition < 4; partition++) { if (partition == 0) { for (size_t i = 0; i < 2; i++) { - R_ABORT_UNLESS(splFsGenerateSpecificAesKey(BisKeySources[partition][i], key_generation, i, bis_keys[partition][i])); + R_ABORT_UNLESS(spl::GenerateSpecificAesKey(bis_keys[partition][i], 0x10, BisKeySources[partition][i], 0x10, key_generation, i)); } } else { const u32 option = (partition == 3 && spl::IsRecoveryBoot()) ? 0x4 : 0x1; - u8 access_key[0x10]; - R_ABORT_UNLESS(splCryptoGenerateAesKek(BisKekSource, key_generation, option, access_key)); + spl::AccessKey access_key; + R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), BisKekSource, 0x10, key_generation, option)); for (size_t i = 0; i < 2; i++) { - R_ABORT_UNLESS(splCryptoGenerateAesKey(access_key, BisKeySources[partition][i], bis_keys[partition][i])); + R_ABORT_UNLESS(spl::GenerateAesKey(bis_keys[partition][i], 0x10, access_key, BisKeySources[partition][i], 0x10)); } } } diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index 9de2611db..88faca823 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -81,7 +81,7 @@ void __appInit(void) { R_ABORT_UNLESS(fsInitialize()); R_ABORT_UNLESS(pmdmntInitialize()); R_ABORT_UNLESS(pminfoInitialize()); - R_ABORT_UNLESS(splFsInitialize()); + spl::InitializeForFs(); }); ams::CheckApiVersion(); @@ -89,7 +89,7 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ - splFsExit(); + spl::Finalize(); pminfoExit(); pmdmntExit(); fsExit(); diff --git a/stratosphere/boot/source/boot_boot_reason.cpp b/stratosphere/boot/source/boot_boot_reason.cpp index 6cb6cb863..0c255434f 100644 --- a/stratosphere/boot/source/boot_boot_reason.cpp +++ b/stratosphere/boot/source/boot_boot_reason.cpp @@ -22,19 +22,6 @@ namespace ams::boot { namespace { - /* Types. */ - struct BootReasonValue { - union { - struct { - u8 power_intr; - u8 rtc_intr; - u8 nv_erc; - u8 boot_reason; - }; - u32 value; - }; - }; - /* Globals. */ u32 g_boot_reason = 0; bool g_detected_boot_reason = false; @@ -90,12 +77,14 @@ namespace ams::boot { /* Set boot reason for SPL. */ if (hos::GetVersion() >= hos::Version_3_0_0) { - BootReasonValue boot_reason_value; - boot_reason_value.power_intr = power_intr; - boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m; - boot_reason_value.nv_erc = nv_erc; + spl::BootReasonValue boot_reason_value = {}; + + boot_reason_value.power_intr = power_intr; + boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m; + boot_reason_value.nv_erc = nv_erc; boot_reason_value.boot_reason = g_boot_reason; - R_ABORT_UNLESS(splSetBootReason(boot_reason_value.value)); + + R_ABORT_UNLESS(spl::SetBootReason(boot_reason_value)); } g_detected_boot_reason = true; diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 9b1a2b346..8d6a78a83 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -66,7 +66,7 @@ namespace ams::boot { /* Globals. */ bool g_is_display_intialized = false; u32 *g_frame_buffer = nullptr; - bool g_is_mariko = false; + spl::SocType g_soc_type = spl::SocType_Erista; u32 g_lcd_vendor = 0; Handle g_dc_das_hnd = INVALID_HANDLE; u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize]; @@ -95,10 +95,9 @@ namespace ams::boot { } inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) { - if (g_is_mariko) { - DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko); - } else { - DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista); + switch (g_soc_type) { + case spl::SocType_Erista: DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista); break; + case spl::SocType_Mariko: DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko); break; } } @@ -188,7 +187,7 @@ namespace ams::boot { void InitializeDisplay() { /* Setup globals. */ InitializeRegisterBaseAddresses(); - g_is_mariko = spl::IsMariko(); + g_soc_type = spl::GetSocType(); InitializeFrameBuffer(); /* Turn on DSI/voltage rail. */ @@ -199,7 +198,7 @@ namespace ams::boot { i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic); - if (g_is_mariko) { + if (g_soc_type == spl::SocType_Mariko) { WriteI2cRegister(i2c_session, 0x18, 0x3A); WriteI2cRegister(i2c_session, 0x1F, 0x71); } @@ -242,7 +241,7 @@ namespace ams::boot { /* Configure display interface and display. */ reg::Write(g_mipi_cal_regs + 0x060, 0); - if (g_is_mariko) { + if (g_soc_type == spl::SocType_Mariko) { reg::Write(g_mipi_cal_regs + 0x058, 0); reg::Write(g_apb_misc_regs + 0xAC0, 0); } @@ -367,7 +366,7 @@ namespace ams::boot { DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11); DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03); DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04); - if (g_is_mariko) { + if (g_soc_type == spl::SocType_Mariko) { /* On Mariko the above configurations are executed twice, for some reason. */ DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11); diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index a6688f24d..50a7b7a2e 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -92,7 +92,7 @@ void __appInit(void) { /* Initialize services we need (TODO: NCM) */ sm::DoWithSession([&]() { R_ABORT_UNLESS(fsInitialize()); - R_ABORT_UNLESS(splInitialize()); + spl::Initialize(); R_ABORT_UNLESS(pmshellInitialize()); }); @@ -102,7 +102,7 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ pmshellExit(); - splExit(); + spl::Finalize(); fsExit(); } diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index c22308e6f..5d0af575e 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -75,7 +75,7 @@ void __appInit(void) { R_ABORT_UNLESS(fsInitialize()); lr::Initialize(); R_ABORT_UNLESS(fsldrInitialize()); - R_ABORT_UNLESS(splInitialize()); + spl::Initialize(); }); ams::CheckApiVersion(); @@ -83,7 +83,7 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ - splExit(); + spl::Finalize(); fsldrExit(); lr::Finalize(); fsExit(); @@ -121,9 +121,9 @@ int main(int argc, char **argv) /* Configure development. */ /* NOTE: Nintendo really does call the getter function three times instead of caching the value. */ - ldr::SetDevelopmentForAcidProductionCheck(spl::IsDevelopmentHardware()); - ldr::SetDevelopmentForAntiDowngradeCheck(spl::IsDevelopmentHardware()); - ldr::SetDevelopmentForAcidSignatureCheck(spl::IsDevelopmentHardware()); + ldr::SetDevelopmentForAcidProductionCheck(spl::IsDevelopment()); + ldr::SetDevelopmentForAntiDowngradeCheck(spl::IsDevelopment()); + ldr::SetDevelopmentForAcidSignatureCheck(spl::IsDevelopment()); /* Add services to manager. */ R_ABORT_UNLESS((g_server_manager.RegisterServer<ldr::pm::ProcessManagerInterface>(ProcessManagerServiceName, ProcessManagerMaxSessions))); diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp index bb8255b32..75c7ac666 100644 --- a/stratosphere/loader/source/ldr_meta.cpp +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -101,13 +101,7 @@ namespace ams::ldr { } const u8 *GetAcidSignatureModulus(u32 key_generation) { - AMS_ASSERT(key_generation <= fssystem::AcidSignatureKeyGenerationMax); - const u32 used_keygen = (key_generation % (fssystem::AcidSignatureKeyGenerationMax + 1)); - if (IsDevelopmentForAcidSignatureCheck()) { - return fssystem::AcidSignatureKeyModulusDev[used_keygen]; - } else { - return fssystem::AcidSignatureKeyModulusProd[used_keygen]; - } + return fssystem::GetAcidSignatureKeyModulus(!IsDevelopmentForAcidSignatureCheck(), key_generation); } Result ValidateAcidSignature(Meta *meta) { @@ -122,8 +116,8 @@ namespace ams::ldr { const size_t sig_size = sizeof(meta->acid->signature); const u8 *mod = GetAcidSignatureModulus(meta->npdm->signature_key_generation); const size_t mod_size = fssystem::AcidSignatureKeyModulusSize; - const u8 *exp = fssystem::AcidSignatureKeyExponent; - const size_t exp_size = fssystem::AcidSignatureKeyExponentSize; + const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent(); + const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize; const u8 *msg = meta->acid->modulus; const size_t msg_size = meta->acid->size; const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index e715e3b3f..780368634 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -224,8 +224,8 @@ namespace ams::ldr { const size_t sig_size = sizeof(code_info.signature); const u8 *mod = static_cast<u8 *>(meta->modulus); const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize; - const u8 *exp = fssystem::AcidSignatureKeyExponent; - const size_t exp_size = fssystem::AcidSignatureKeyExponentSize; + const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent(); + const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize; const u8 *hsh = code_info.hash; const size_t hsh_size = sizeof(code_info.hash); const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size); diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index 8bb8760e2..8d16556c1 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -96,7 +96,7 @@ void __appInit(void) { sm::DoWithSession([&]() { R_ABORT_UNLESS(fsInitialize()); - R_ABORT_UNLESS(splInitialize()); + spl::Initialize(); }); ams::CheckApiVersion(); @@ -104,7 +104,7 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ - splExit(); + spl::Finalize(); fsExit(); } diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 3efc9cabc..ae7078c32 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -143,7 +143,7 @@ void __appInit(void) { R_ABORT_UNLESS(sm::manager::EndInitialDefers()); R_ABORT_UNLESS(ldrPmInitialize()); - R_ABORT_UNLESS(splInitialize()); + spl::Initialize(); }); ams::CheckApiVersion(); @@ -151,7 +151,7 @@ void __appInit(void) { void __appExit(void) { /* Cleanup services. */ - splExit(); + spl::Finalize(); ldrPmExit(); smManagerExit(); fsprExit(); diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index aa0a1e912..2a0a8034c 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -64,7 +64,7 @@ void __appInit(void) { sm::DoWithSession([&]() { R_ABORT_UNLESS(setsysInitialize()); R_ABORT_UNLESS(fsInitialize()); - R_ABORT_UNLESS(splInitialize()); + spl::Initialize(); if (hos::GetVersion() < hos::Version_3_0_0) { R_ABORT_UNLESS(pminfoInitialize()); } @@ -80,6 +80,7 @@ void __appExit(void) { if (hos::GetVersion() < hos::Version_3_0_0) { pminfoExit(); } + setsysExit(); } @@ -114,9 +115,9 @@ int main(int argc, char **argv) /* Initialize Debug config. */ { - ON_SCOPE_EXIT { splExit(); }; + ON_SCOPE_EXIT { spl::Finalize(); }; - ro::SetDevelopmentHardware(spl::IsDevelopmentHardware()); + ro::SetDevelopmentHardware(spl::IsDevelopment()); ro::SetDevelopmentFunctionEnabled(spl::IsDevelopmentFunctionEnabled()); } diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index 49592e8b1..f4c420070 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -15,8 +15,8 @@ */ #include <stratosphere.hpp> #include "spl_api_impl.hpp" - #include "spl_ctr_drbg.hpp" +#include "spl_key_slot_cache.hpp" namespace ams::spl::impl { @@ -35,21 +35,161 @@ namespace ams::spl::impl { constexpr size_t WorkBufferSizeMax = 0x800; - constexpr size_t MaxAesKeyslots = 6; - constexpr size_t MaxAesKeyslotsDeprecated = 4; + constexpr s32 MaxPhysicalAesKeyslots = 6; + constexpr s32 MaxPhysicalAesKeyslotsDeprecated = 4; - /* Max Keyslots helper. */ - inline size_t GetMaxKeyslots() { - return (hos::GetVersion() >= hos::Version_6_0_0) ? MaxAesKeyslots : MaxAesKeyslotsDeprecated; + constexpr s32 MaxVirtualAesKeyslots = 9; + + /* Keyslot management. */ + KeySlotCache g_keyslot_cache; + std::optional<KeySlotCacheEntry> g_keyslot_cache_entry[MaxPhysicalAesKeyslots]; + + inline s32 GetMaxPhysicalKeyslots() { + return (hos::GetVersion() >= hos::Version_6_0_0) ? MaxPhysicalAesKeyslots : MaxPhysicalAesKeyslotsDeprecated; + } + + constexpr s32 VirtualKeySlotMin = 16; + constexpr s32 VirtualKeySlotMax = VirtualKeySlotMin + MaxVirtualAesKeyslots - 1; + + constexpr inline bool IsVirtualKeySlot(s32 keyslot) { + return VirtualKeySlotMin <= keyslot && keyslot <= VirtualKeySlotMax; + } + + inline bool IsPhysicalKeySlot(s32 keyslot) { + return keyslot < GetMaxPhysicalKeyslots(); + } + + constexpr inline s32 GetVirtualKeySlotIndex(s32 keyslot) { + AMS_ASSERT(IsVirtualKeySlot(keyslot)); + return keyslot - VirtualKeySlotMin; + } + + constexpr inline s32 MakeVirtualKeySlot(s32 index) { + const s32 virt_slot = index + VirtualKeySlotMin; + AMS_ASSERT(IsVirtualKeySlot(virt_slot)); + return virt_slot; + } + + void InitializeKeySlotCache() { + for (s32 i = 0; i < MaxPhysicalAesKeyslots; i++) { + g_keyslot_cache_entry[i].emplace(i); + g_keyslot_cache.AddEntry(std::addressof(g_keyslot_cache_entry[i].value())); + } + } + + enum class KeySlotContentType { + None = 0, + AesKey = 1, + TitleKey = 2, + }; + + struct KeySlotContents { + KeySlotContentType type; + union { + struct { + AccessKey access_key; + KeySource key_source; + } aes_key; + struct { + AccessKey access_key; + } title_key; + }; + }; + + const void *g_keyslot_owners[MaxVirtualAesKeyslots]; + KeySlotContents g_keyslot_contents[MaxVirtualAesKeyslots]; + KeySlotContents g_physical_keyslot_contents_for_backwards_compatibility[MaxPhysicalAesKeyslots]; + + void ClearPhysicalKeyslot(s32 keyslot) { + AMS_ASSERT(IsPhysicalKeySlot(keyslot)); + + AccessKey access_key = {}; + KeySource key_source = {}; + smc::LoadAesKey(keyslot, access_key, key_source); + } + + s32 GetPhysicalKeySlot(s32 keyslot, bool load) { + s32 phys_slot = -1; + KeySlotContents *contents = nullptr; + + if (hos::GetVersion() == hos::Version_1_0_0 && IsPhysicalKeySlot(keyslot)) { + /* On 1.0.0, we allow the use of physical keyslots. */ + phys_slot = keyslot; + contents = std::addressof(g_physical_keyslot_contents_for_backwards_compatibility[phys_slot]); + + /* If the physical slot is already loaded, we're good. */ + if (g_keyslot_cache.FindPhysical(phys_slot)) { + return phys_slot; + } + } else { + /* This should be a virtual keyslot. */ + AMS_ASSERT(IsVirtualKeySlot(keyslot)); + + /* Try to find a physical slot in the cache. */ + if (g_keyslot_cache.Find(std::addressof(phys_slot), keyslot)) { + return phys_slot; + } + + /* Allocate a physical slot. */ + phys_slot = g_keyslot_cache.Allocate(keyslot); + contents = std::addressof(g_keyslot_contents[GetVirtualKeySlotIndex(keyslot)]); + } + + /* Ensure the contents of the keyslot. */ + if (load) { + switch (contents->type) { + case KeySlotContentType::None: + ClearPhysicalKeyslot(phys_slot); + break; + case KeySlotContentType::AesKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadAesKey(phys_slot, contents->aes_key.access_key, contents->aes_key.key_source))); + break; + case KeySlotContentType::TitleKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadTitleKey(phys_slot, contents->title_key.access_key))); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return phys_slot; + } + + Result LoadVirtualAesKey(s32 keyslot, const AccessKey &access_key, const KeySource &key_source) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadAesKey(phys_slot, access_key, key_source))); + + /* Update our contents. */ + const s32 index = GetVirtualKeySlotIndex(keyslot); + + g_keyslot_contents[index].type = KeySlotContentType::AesKey; + g_keyslot_contents[index].aes_key.access_key = access_key; + g_keyslot_contents[index].aes_key.key_source = key_source; + + return ResultSuccess(); + } + + Result LoadVirtualTitleKey(s32 keyslot, const AccessKey &access_key) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadTitleKey(phys_slot, access_key))); + + /* Update our contents. */ + const s32 index = GetVirtualKeySlotIndex(keyslot); + + g_keyslot_contents[index].type = KeySlotContentType::TitleKey; + g_keyslot_contents[index].title_key.access_key = access_key; + + return ResultSuccess(); } /* Type definitions. */ class ScopedAesKeyslot { private: - u32 slot; + s32 slot; bool has_slot; public: - ScopedAesKeyslot() : slot(0), has_slot(false) { + ScopedAesKeyslot() : slot(-1), has_slot(false) { /* ... */ } ~ScopedAesKeyslot() { @@ -58,7 +198,7 @@ namespace ams::spl::impl { } } - u32 GetKeyslot() const { + u32 GetKeySlot() const { return this->slot; } @@ -107,7 +247,6 @@ namespace ams::spl::impl { os::Mutex g_async_op_lock(false); - const void *g_keyslot_owners[MaxAesKeyslots]; BootReasonValue g_boot_reason; bool g_boot_reason_set; @@ -202,14 +341,21 @@ namespace ams::spl::impl { } /* Internal Keyslot utility. */ - Result ValidateAesKeyslot(u32 keyslot, const void *owner) { - R_UNLESS(keyslot < GetMaxKeyslots(), spl::ResultInvalidKeyslot()); - R_UNLESS((g_keyslot_owners[keyslot] == owner || hos::GetVersion() == hos::Version_1_0_0), spl::ResultInvalidKeyslot()); + Result ValidateAesKeyslot(s32 keyslot, const void *owner) { + /* Allow the use of physical keyslots on 1.0.0. */ + if (hos::GetVersion() == hos::Version_1_0_0) { + R_SUCCEED_IF(IsPhysicalKeySlot(keyslot)); + } + + R_UNLESS(IsVirtualKeySlot(keyslot), spl::ResultInvalidKeyslot()); + + const s32 index = GetVirtualKeySlotIndex(keyslot); + R_UNLESS(g_keyslot_owners[index] == owner, spl::ResultInvalidKeyslot()); return ResultSuccess(); } /* Helper to do a single AES block decryption. */ - smc::Result DecryptAesBlock(u32 keyslot, void *dst, const void *src) { + smc::Result DecryptAesBlock(s32 keyslot, void *dst, const void *src) { struct DecryptAesBlockLayout { SeCryptContext crypt_ctx; u8 in_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); @@ -231,7 +377,7 @@ namespace ams::spl::impl { std::scoped_lock lk(g_async_op_lock); smc::AsyncOperationKey op_key; const IvCtr iv_ctr = {}; - const u32 mode = smc::GetCryptAesMode(smc::CipherMode::CbcDecrypt, keyslot); + const u32 mode = smc::GetCryptAesMode(smc::CipherMode::CbcDecrypt, GetPhysicalKeySlot(keyslot, true)); const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.out); const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.in); @@ -363,6 +509,8 @@ namespace ams::spl::impl { InitializeSeEvents(); /* Initialize DAS for the SE. */ InitializeDeviceAddressSpace(); + /* Initialize the keyslot cache. */ + InitializeKeySlotCache(); } /* General. */ @@ -474,14 +622,12 @@ namespace ams::spl::impl { return smc::ConvertResult(smc::GenerateAesKek(out_access_key, key_source, generation, option)); } - Result LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { + Result LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { R_TRY(ValidateAesKeyslot(keyslot, owner)); - return smc::ConvertResult(smc::LoadAesKey(keyslot, access_key, key_source)); + return LoadVirtualAesKey(keyslot, access_key, key_source); } Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { - smc::Result smc_rc; - static constexpr KeySource s_generate_aes_key_source = { .data = {0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8} }; @@ -489,12 +635,9 @@ namespace ams::spl::impl { ScopedAesKeyslot keyslot_holder; R_TRY(keyslot_holder.Allocate()); - smc_rc = smc::LoadAesKey(keyslot_holder.GetKeyslot(), access_key, s_generate_aes_key_source); - if (smc_rc == smc::Result::Success) { - smc_rc = DecryptAesBlock(keyslot_holder.GetKeyslot(), out_key, &key_source); - } + R_TRY(LoadVirtualAesKey(keyslot_holder.GetKeySlot(), access_key, s_generate_aes_key_source)); - return smc::ConvertResult(smc_rc); + return smc::ConvertResult(DecryptAesBlock(keyslot_holder.GetKeySlot(), out_key, &key_source)); } Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { @@ -508,7 +651,7 @@ namespace ams::spl::impl { return GenerateAesKey(out_key, access_key, key_source); } - Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { + Result CryptAesCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { R_TRY(ValidateAesKeyslot(keyslot, owner)); /* Succeed immediately if there's nothing to crypt. */ @@ -555,7 +698,7 @@ namespace ams::spl::impl { { std::scoped_lock lk(g_async_op_lock); smc::AsyncOperationKey op_key; - const u32 mode = smc::GetCryptAesMode(smc::CipherMode::Ctr, keyslot); + const u32 mode = smc::GetCryptAesMode(smc::CipherMode::Ctr, GetPhysicalKeySlot(keyslot, true)); const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, out); const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, in); @@ -573,26 +716,22 @@ namespace ams::spl::impl { return ResultSuccess(); } - Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size) { + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size) { R_TRY(ValidateAesKeyslot(keyslot, owner)); R_UNLESS(size <= WorkBufferSizeMax, spl::ResultInvalidSize()); std::memcpy(g_work_buffer, data, size); - return smc::ConvertResult(smc::ComputeCmac(out_cmac, keyslot, g_work_buffer, size)); + return smc::ConvertResult(smc::ComputeCmac(out_cmac, GetPhysicalKeySlot(keyslot, true), g_work_buffer, size)); } - Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner) { - if (hos::GetVersion() <= hos::Version_1_0_0) { - /* On 1.0.0, keyslots were kind of a wild west. */ - *out_keyslot = 0; - return ResultSuccess(); - } - - for (size_t i = 0; i < GetMaxKeyslots(); i++) { - if (g_keyslot_owners[i] == 0) { - g_keyslot_owners[i] = owner; - *out_keyslot = static_cast<u32>(i); + Result AllocateAesKeyslot(s32 *out_keyslot, const void *owner) { + /* Find a virtual keyslot. */ + for (s32 i = 0; i < MaxVirtualAesKeyslots; i++) { + if (g_keyslot_owners[i] == nullptr) { + g_keyslot_owners[i] = owner; + g_keyslot_contents[i] = { .type = KeySlotContentType::None }; + *out_keyslot = MakeVirtualKeySlot(i); return ResultSuccess(); } } @@ -601,22 +740,24 @@ namespace ams::spl::impl { return spl::ResultOutOfKeyslots(); } - Result FreeAesKeyslot(u32 keyslot, const void *owner) { - if (hos::GetVersion() <= hos::Version_1_0_0) { - /* On 1.0.0, keyslots were kind of a wild west. */ - return ResultSuccess(); - } + Result FreeAesKeyslot(s32 keyslot, const void *owner) { + /* Only virtual keyslots can be freed. */ + R_UNLESS(IsVirtualKeySlot(keyslot), spl::ResultInvalidKeyslot()); + /* Ensure the keyslot is owned. */ R_TRY(ValidateAesKeyslot(keyslot, owner)); - /* Clear the keyslot. */ - { - AccessKey access_key = {}; - KeySource key_source = {}; - - smc::LoadAesKey(keyslot, access_key, key_source); + /* Clear the physical keyslot, if we're cached. */ + s32 phys_slot; + if (g_keyslot_cache.Release(std::addressof(phys_slot), keyslot)) { + ClearPhysicalKeyslot(phys_slot); } - g_keyslot_owners[keyslot] = nullptr; + + /* Clear the virtual keyslot. */ + const auto index = GetVirtualKeySlotIndex(keyslot); + g_keyslot_owners[index] = nullptr; + g_keyslot_contents[index].type = KeySlotContentType::None; + os::SignalSystemEvent(std::addressof(g_se_keyslot_available_event)); return ResultSuccess(); } @@ -702,7 +843,7 @@ namespace ams::spl::impl { return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, smc::EsKeyType::ElicenseKey); } - Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result LoadElicenseKey(s32 keyslot, const void *owner, const AccessKey &access_key) { /* Right now, this is just literally the same function as LoadTitleKey in N's impl. */ return LoadTitleKey(keyslot, owner, access_key); } @@ -731,9 +872,9 @@ namespace ams::spl::impl { return smc::ConvertResult(smc::GenerateSpecificAesKey(out_key, key_source, generation, which)); } - Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result LoadTitleKey(s32 keyslot, const void *owner, const AccessKey &access_key) { R_TRY(ValidateAesKeyslot(keyslot, owner)); - return smc::ConvertResult(smc::LoadTitleKey(keyslot, access_key)); + return LoadVirtualTitleKey(keyslot, access_key); } Result GetPackage2Hash(void *dst, const size_t size) { @@ -784,9 +925,9 @@ namespace ams::spl::impl { /* Helper. */ Result FreeAesKeyslots(const void *owner) { - for (size_t i = 0; i < GetMaxKeyslots(); i++) { - if (g_keyslot_owners[i] == owner) { - FreeAesKeyslot(i, owner); + for (s32 slot = VirtualKeySlotMin; slot <= VirtualKeySlotMax; ++slot) { + if (g_keyslot_owners[GetVirtualKeySlotIndex(slot)] == owner) { + FreeAesKeyslot(slot, owner); } } return ResultSuccess(); diff --git a/stratosphere/spl/source/spl_api_impl.hpp b/stratosphere/spl/source/spl_api_impl.hpp index 9422104ea..483f29e53 100644 --- a/stratosphere/spl/source/spl_api_impl.hpp +++ b/stratosphere/spl/source/spl_api_impl.hpp @@ -32,13 +32,13 @@ namespace ams::spl::impl { /* Crypto. */ Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); - Result LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source); + Result LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source); Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source); Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option); - Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr); - Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size); - Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner); - Result FreeAesKeyslot(u32 keyslot, const void *owner); + Result CryptAesCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr); + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size); + Result AllocateAesKeyslot(s32 *out_keyslot, const void *owner); + Result FreeAesKeyslot(s32 keyslot, const void *owner); /* RSA. */ Result DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); @@ -54,13 +54,13 @@ namespace ams::spl::impl { Result ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); Result DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); Result UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); - Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result LoadElicenseKey(s32 keyslot, const void *owner, const AccessKey &access_key); /* FS */ Result ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); Result DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size); Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); - Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result LoadTitleKey(s32 keyslot, const void *owner, const AccessKey &access_key); Result GetPackage2Hash(void *dst, const size_t size); /* Manu. */ diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp index 4bd5bbe06..63b150a43 100644 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -28,7 +28,7 @@ namespace ams::spl { return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); } - Result CryptoService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + Result CryptoService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { return impl::LoadAesKey(keyslot, this, access_key, key_source); } @@ -40,19 +40,19 @@ namespace ams::spl { return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); } - Result CryptoService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + Result CryptoService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result CryptoService::ComputeCmac(sf::Out<Cmac> out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf) { + Result CryptoService::ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { return impl::ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); } - Result CryptoService::AllocateAesKeyslot(sf::Out<u32> out_keyslot) { + Result CryptoService::AllocateAesKeyslot(sf::Out<s32> out_keyslot) { return impl::AllocateAesKeyslot(out_keyslot.GetPointer(), this); } - Result CryptoService::FreeAesKeyslot(u32 keyslot) { + Result CryptoService::FreeAesKeyslot(s32 keyslot) { return impl::FreeAesKeyslot(keyslot, this); } diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp index 58841575c..8eec93dd8 100644 --- a/stratosphere/spl/source/spl_crypto_service.hpp +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -25,13 +25,13 @@ namespace ams::spl { protected: /* Actual commands. */ virtual Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option); - virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); virtual Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source); virtual Result DecryptAesKey(sf::Out<AesKey> out_key, KeySource key_source, u32 generation, u32 option); - virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - virtual Result ComputeCmac(sf::Out<Cmac> out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf); - virtual Result AllocateAesKeyslot(sf::Out<u32> out_keyslot); - virtual Result FreeAesKeyslot(u32 keyslot); + virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); + virtual Result AllocateAesKeyslot(sf::Out<s32> out_keyslot); + virtual Result FreeAesKeyslot(s32 keyslot); virtual void GetAesKeyslotAvailableEvent(sf::OutCopyHandle out_hnd); public: DEFINE_SERVICE_DISPATCH_TABLE { @@ -48,9 +48,9 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(DecryptAesKey), MAKE_SERVICE_COMMAND_META(CryptAesCtr), MAKE_SERVICE_COMMAND_META(ComputeCmac), - MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(FreeAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), }; }; diff --git a/stratosphere/spl/source/spl_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp index 545c6a2b5..f6c4ec8c8 100644 --- a/stratosphere/spl/source/spl_deprecated_service.cpp +++ b/stratosphere/spl/source/spl_deprecated_service.cpp @@ -31,7 +31,7 @@ namespace ams::spl { return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); } - Result DeprecatedService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + Result DeprecatedService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { return impl::LoadAesKey(keyslot, this, access_key, key_source); } @@ -71,15 +71,15 @@ namespace ams::spl { return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); } - Result DeprecatedService::CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, u32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { + Result DeprecatedService::CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result DeprecatedService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + Result DeprecatedService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result DeprecatedService::ComputeCmac(sf::Out<Cmac> out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf) { + Result DeprecatedService::ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { return impl::ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); } @@ -95,7 +95,7 @@ namespace ams::spl { return impl::UnwrapTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); } - Result DeprecatedService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + Result DeprecatedService::LoadTitleKey(s32 keyslot, AccessKey access_key) { return impl::LoadTitleKey(keyslot, this, access_key); } @@ -107,11 +107,11 @@ namespace ams::spl { return impl::UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); } - Result DeprecatedService::AllocateAesKeyslot(sf::Out<u32> out_keyslot) { + Result DeprecatedService::AllocateAesKeyslot(sf::Out<s32> out_keyslot) { return impl::AllocateAesKeyslot(out_keyslot.GetPointer(), this); } - Result DeprecatedService::FreeAesKeyslot(u32 keyslot) { + Result DeprecatedService::FreeAesKeyslot(s32 keyslot) { return impl::FreeAesKeyslot(keyslot, this); } diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp index 6274ed336..76c86138b 100644 --- a/stratosphere/spl/source/spl_deprecated_service.hpp +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -63,7 +63,7 @@ namespace ams::spl { virtual Result GetConfig(sf::Out<u64> out, u32 which); virtual Result ExpMod(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); virtual Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option); - virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); virtual Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source); virtual Result SetConfig(u32 which, u64 value); virtual Result GenerateRandomBytes(const sf::OutPointerBuffer &out); @@ -73,17 +73,17 @@ namespace ams::spl { virtual Result GenerateSpecificAesKey(sf::Out<AesKey> out_key, KeySource key_source, u32 generation, u32 which); virtual Result DecryptRsaPrivateKey(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); virtual Result DecryptAesKey(sf::Out<AesKey> out_key, KeySource key_source, u32 generation, u32 option); - virtual Result CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, u32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); - virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - virtual Result ComputeCmac(sf::Out<Cmac> out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf); + virtual Result CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); + virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); virtual Result ImportEsKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); virtual Result UnwrapTitleKeyDeprecated(sf::Out<AccessKey> out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); virtual Result UnwrapTitleKey(sf::Out<AccessKey> out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result LoadTitleKey(s32 keyslot, AccessKey access_key); virtual Result UnwrapCommonTitleKeyDeprecated(sf::Out<AccessKey> out_access_key, KeySource key_source); virtual Result UnwrapCommonTitleKey(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation); - virtual Result AllocateAesKeyslot(sf::Out<u32> out_keyslot); - virtual Result FreeAesKeyslot(u32 keyslot); + virtual Result AllocateAesKeyslot(sf::Out<s32> out_keyslot); + virtual Result FreeAesKeyslot(s32 keyslot); virtual void GetAesKeyslotAvailableEvent(sf::OutCopyHandle out_hnd); virtual Result SetBootReason(BootReasonValue boot_reason); virtual Result GetBootReason(sf::Out<BootReasonValue> out); @@ -117,9 +117,9 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKeyDeprecated, hos::Version_2_0_0, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKey, hos::Version_3_0_0), - MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(FreeAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), MAKE_SERVICE_COMMAND_META(SetBootReason, hos::Version_3_0_0), MAKE_SERVICE_COMMAND_META(GetBootReason, hos::Version_3_0_0), diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp index 4c9e08952..9f1a9cffc 100644 --- a/stratosphere/spl/source/spl_es_service.cpp +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -47,7 +47,7 @@ namespace ams::spl { return impl::UnwrapElicenseKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); } - Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { + Result EsService::LoadElicenseKey(s32 keyslot, AccessKey access_key) { return impl::LoadElicenseKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 82989d4ba..829cc4a86 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -31,7 +31,7 @@ namespace ams::spl { virtual Result ImportDrmKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); virtual Result DrmExpMod(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod); virtual Result UnwrapElicenseKey(sf::Out<AccessKey> out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); + virtual Result LoadElicenseKey(s32 keyslot, AccessKey access_key); public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(GetConfig), diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index 8d3c9ce8b..290e4b177 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -35,7 +35,7 @@ namespace ams::spl { return impl::GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); } - Result FsService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + Result FsService::LoadTitleKey(s32 keyslot, AccessKey access_key) { return impl::LoadTitleKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 7b13303ee..b9a48781f 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -28,7 +28,7 @@ namespace ams::spl { virtual Result ImportLotusKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); virtual Result DecryptLotusMessage(sf::Out<u32> out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); virtual Result GenerateSpecificAesKey(sf::Out<AesKey> out_key, KeySource key_source, u32 generation, u32 which); - virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result LoadTitleKey(s32 keyslot, AccessKey access_key); virtual Result GetPackage2Hash(const sf::OutPointerBuffer &dst); public: DEFINE_SERVICE_DISPATCH_TABLE { diff --git a/stratosphere/spl/source/spl_key_slot_cache.hpp b/stratosphere/spl/source/spl_key_slot_cache.hpp new file mode 100644 index 000000000..18c093dc8 --- /dev/null +++ b/stratosphere/spl/source/spl_key_slot_cache.hpp @@ -0,0 +1,138 @@ +/* + * 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/>. + */ +#pragma once +#include <stratosphere.hpp> + +namespace ams::spl { + + class KeySlotCacheEntry : public util::IntrusiveListBaseNode<KeySlotCacheEntry> { + NON_COPYABLE(KeySlotCacheEntry); + NON_MOVEABLE(KeySlotCacheEntry); + private: + friend class KeySlotCache; + public: + static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; + private: + const s32 slot_index; + s32 virtual_slot; + public: + explicit KeySlotCacheEntry(s32 idx) : slot_index(idx), virtual_slot(-1) { /* ... */ } + + bool Contains(s32 virtual_slot) const { + return virtual_slot == this->virtual_slot; + } + + s32 GetPhysicalKeySlotIndex() const { return this->slot_index; } + + s32 GetVirtualKeySlotIndex() const { return this->virtual_slot; } + + void SetVirtualSlot(s32 virtual_slot) { + this->virtual_slot = virtual_slot; + } + + void ClearVirtualSlot() { + this->virtual_slot = -1; + } + }; + + class KeySlotCache { + NON_COPYABLE(KeySlotCache); + NON_MOVEABLE(KeySlotCache); + private: + using KeySlotCacheEntryList = util::IntrusiveListBaseTraits<KeySlotCacheEntry>::ListType; + private: + KeySlotCacheEntryList mru_list; + public: + constexpr KeySlotCache() : mru_list() { /* ... */ } + + s32 Allocate(s32 virtual_slot) { + return this->AllocateFromLru(virtual_slot); + } + + bool Find(s32 *out, s32 virtual_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalKeySlotIndex(); + + this->UpdateMru(it); + return true; + } + } + + return false; + } + + bool Release(s32 *out, s32 virtual_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalKeySlotIndex(); + it->ClearVirtualSlot(); + + this->UpdateLru(it); + return true; + } + } + + return false; + } + + bool FindPhysical(s32 physical_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->GetPhysicalKeySlotIndex() == physical_slot) { + this->UpdateMru(it); + + if (it->GetVirtualKeySlotIndex() == physical_slot) { + return true; + } else { + it->SetVirtualSlot(physical_slot); + return false; + } + } + } + AMS_ABORT(); + } + + void AddEntry(KeySlotCacheEntry *entry) { + this->mru_list.push_front(*entry); + } + private: + s32 AllocateFromLru(s32 virtual_slot) { + AMS_ASSERT(!this->mru_list.empty()); + + auto it = this->mru_list.rbegin(); + it->SetVirtualSlot(virtual_slot); + + auto *entry = std::addressof(*it); + this->mru_list.pop_back(); + this->mru_list.push_front(*entry); + + return entry->GetPhysicalKeySlotIndex(); + } + + void UpdateMru(KeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + this->mru_list.erase(it); + this->mru_list.push_front(*entry); + } + + void UpdateLru(KeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + this->mru_list.erase(it); + this->mru_list.push_back(*entry); + } + }; + +} From 311d2678c7b5b4067230abe75114f0226e5da6c0 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 15:44:47 -0700 Subject: [PATCH 027/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "b38939ad" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "b38939ad" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index e71fbde40..aafd0bc01 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = 6913aa52953f228f7abc7cc7617a6ae6baec1eca - parent = 2dfe5b192eebe837f06866fbe92b44209cd4b913 + commit = b38939adb58c8a28eb9556aa7abbcbed4d823c43 + parent = 81f91803ecde50a5007729ff36c7ee2b2357b4bf method = merge cmdver = 0.4.1 From 53a47e07fd4b3270f11c6eaec5cff0f4865cf085 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 14 May 2020 02:22:24 -0700 Subject: [PATCH 028/118] nca-pr: update for c++20 --- .../stratosphere/fs/fs_memory_management.hpp | 2 +- .../stratosphere/fs/fs_save_data_types.hpp | 2 +- .../fssrv/fssrv_i_file_system_creator.hpp | 2 +- ...ystem_aes_ctr_counter_extended_storage.hpp | 2 +- .../fssystem/fssystem_bucket_tree.hpp | 10 ++++----- .../fssystem_bucket_tree_template_impl.hpp | 4 ++-- .../fssystem/fssystem_indirect_storage.hpp | 6 ++--- .../fssystem_nca_file_system_driver.hpp | 2 +- .../fssystem/fssystem_nca_header.hpp | 22 +++++++++---------- .../fssystem_block_cache_buffered_storage.hpp | 4 ++-- ...rchical_integrity_verification_storage.hpp | 10 ++++----- ...ssystem_integrity_verification_storage.hpp | 2 +- .../source/fssystem/fssystem_bucket_tree.cpp | 2 +- .../save/fssystem_buffered_storage.cpp | 2 +- 14 files changed, 36 insertions(+), 36 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp index abe362650..7084daeab 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_memory_management.hpp @@ -50,7 +50,7 @@ namespace ams::fs { std::unique_ptr<ArrayT, Deleter> MakeUnique(size_t size) { using T = typename std::remove_extent<ArrayT>::type; - static_assert(std::is_pod<ArrayT>::value); + static_assert(util::is_pod<ArrayT>::value); static_assert(std::is_array<ArrayT>::value); const size_t alloc_size = sizeof(T) * size; diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp index 83387f837..38690d34f 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_types.hpp @@ -161,7 +161,7 @@ namespace ams::fs { u8 value[Size]; }; - static_assert(std::is_pod<HashSalt>::value); + static_assert(util::is_pod<HashSalt>::value); static_assert(sizeof(HashSalt) == HashSalt::Size); using SaveDataHashSalt = std::optional<HashSalt>; diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp index 9b6235980..4e8e5c99f 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp @@ -73,6 +73,6 @@ namespace ams::fssrv::fscreator { IStorageOnNcaCreator *storage_on_nca_creator; /* TODO: More creators. */ }; - static_assert(std::is_pod<FileSystemCreatorInterfaces>::value); + static_assert(util::is_pod<FileSystemCreatorInterfaces>::value); } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp index d1e198c85..6aff1da55 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp @@ -57,7 +57,7 @@ namespace ams::fssystem { }; static_assert(sizeof(Entry) == 0x10); static_assert(alignof(Entry) == 4); - static_assert(std::is_pod<Entry>::value); + static_assert(util::is_pod<Entry>::value); public: static constexpr s64 QueryHeaderStorageSize() { return BucketTree::QueryHeaderStorageSize(); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp index d01168e0b..fdf718b4d 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp @@ -40,7 +40,7 @@ namespace ams::fssystem { void Format(s32 entry_count); Result Verify() const; }; - static_assert(std::is_pod<Header>::value); + static_assert(util::is_pod<Header>::value); static_assert(sizeof(Header) == 0x10); struct NodeHeader { @@ -50,7 +50,7 @@ namespace ams::fssystem { Result Verify(s32 node_index, size_t node_size, size_t entry_size) const; }; - static_assert(std::is_pod<NodeHeader>::value); + static_assert(util::is_pod<NodeHeader>::value); static_assert(sizeof(NodeHeader) == 0x10); class ContinuousReadingInfo { @@ -140,7 +140,7 @@ namespace ams::fssystem { template<typename T> T *Get() const { - static_assert(std::is_pod<T>::value); + static_assert(util::is_pod<T>::value); static_assert(sizeof(T) == sizeof(NodeHeader)); return reinterpret_cast<T *>(this->header); } @@ -278,9 +278,9 @@ namespace ams::fssystem { s64 end; s64 start; } info; - static_assert(std::is_pod<Info>::value); + static_assert(util::is_pod<Info>::value); }; - static_assert(std::is_pod<EntrySetHeader>::value); + static_assert(util::is_pod<EntrySetHeader>::value); private: const BucketTree *tree; void *entry; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp index 1fcb6d903..03d55e42b 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree_template_impl.hpp @@ -22,7 +22,7 @@ namespace ams::fssystem { template<typename EntryType> Result BucketTree::ScanContinuousReading(ContinuousReadingInfo *out_info, const ContinuousReadingParam<EntryType> ¶m) const { - static_assert(std::is_pod<ContinuousReadingParam<EntryType>>::value); + static_assert(util::is_pod<ContinuousReadingParam<EntryType>>::value); /* Validate our preconditions. */ AMS_ASSERT(this->IsInitialized()); @@ -149,7 +149,7 @@ namespace ams::fssystem { template<typename EntryType> Result BucketTree::Visitor::ScanContinuousReading(ContinuousReadingInfo *out_info, s64 offset, size_t size) const { - static_assert(std::is_pod<EntryType>::value); + static_assert(util::is_pod<EntryType>::value); AMS_ASSERT(this->IsValid()); /* Create our parameters. */ diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp index 0f35e1640..0fdde75a2 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_indirect_storage.hpp @@ -53,7 +53,7 @@ namespace ams::fssystem { return offset; } }; - static_assert(std::is_pod<Entry>::value); + static_assert(util::is_pod<Entry>::value); static_assert(sizeof(Entry) == 0x14); struct EntryData { @@ -67,7 +67,7 @@ namespace ams::fssystem { this->storage_index = entry.storage_index; } }; - static_assert(std::is_pod<EntryData>::value); + static_assert(util::is_pod<EntryData>::value); private: struct ContinuousReadingEntry { static constexpr size_t FragmentSizeMax = 4_KB; @@ -86,7 +86,7 @@ namespace ams::fssystem { return this->entry.storage_index != 0; } }; - static_assert(std::is_pod<ContinuousReadingEntry>::value); + static_assert(util::is_pod<ContinuousReadingEntry>::value); public: static constexpr s64 QueryHeaderStorageSize() { return BucketTree::QueryHeaderStorageSize(); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp index 10460c363..4f4d9576c 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp @@ -55,7 +55,7 @@ namespace ams::fssystem { DecryptAesCtrFunction decrypt_aes_ctr_external; bool is_plaintext_header_available; }; - static_assert(std::is_pod<NcaCryptoConfiguration>::value); + static_assert(util::is_pod<NcaCryptoConfiguration>::value); constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) { return key_type < 0; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp index e949dc976..bc3cc6cb0 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp @@ -23,7 +23,7 @@ namespace ams::fssystem { u8 value[Size]; }; static_assert(sizeof(Hash) == Hash::Size); - static_assert(std::is_pod<Hash>::value); + static_assert(util::is_pod<Hash>::value); using NcaDigest = Hash; @@ -70,7 +70,7 @@ namespace ams::fssystem { u32 reserved; }; static_assert(sizeof(FsInfo) == 0x10); - static_assert(std::is_pod<FsInfo>::value); + static_assert(util::is_pod<FsInfo>::value); static constexpr u32 Magic0 = util::FourCC<'N','C','A','0'>::Code; static constexpr u32 Magic1 = util::FourCC<'N','C','A','1'>::Code; @@ -124,7 +124,7 @@ namespace ams::fssystem { u8 GetProperKeyGeneration() const; }; static_assert(sizeof(NcaHeader) == NcaHeader::Size); - static_assert(std::is_pod<NcaHeader>::value); + static_assert(util::is_pod<NcaHeader>::value); struct NcaBucketInfo { static constexpr size_t HeaderSize = 0x10; @@ -132,7 +132,7 @@ namespace ams::fssystem { s64 size; u8 header[HeaderSize]; }; - static_assert(std::is_pod<NcaBucketInfo>::value); + static_assert(util::is_pod<NcaBucketInfo>::value); struct NcaPatchInfo { static constexpr size_t Size = 0x40; @@ -148,7 +148,7 @@ namespace ams::fssystem { bool HasIndirectTable() const; bool HasAesCtrExTable() const; }; - static_assert(std::is_pod<NcaPatchInfo>::value); + static_assert(util::is_pod<NcaPatchInfo>::value); union NcaAesCtrUpperIv { u64 value; @@ -157,7 +157,7 @@ namespace ams::fssystem { u32 secure_value; } part; }; - static_assert(std::is_pod<NcaAesCtrUpperIv>::value); + static_assert(util::is_pod<NcaAesCtrUpperIv>::value); struct NcaSparseInfo { NcaBucketInfo bucket; @@ -179,7 +179,7 @@ namespace ams::fssystem { return sparse_upper_iv; } }; - static_assert(std::is_pod<NcaSparseInfo>::value); + static_assert(util::is_pod<NcaSparseInfo>::value); struct NcaFsHeader { static constexpr size_t Size = 0x200; @@ -189,7 +189,7 @@ namespace ams::fssystem { s64 offset; s64 size; }; - static_assert(std::is_pod<Region>::value); + static_assert(util::is_pod<Region>::value); enum class FsType : u8 { RomFs = 0, @@ -221,7 +221,7 @@ namespace ams::fssystem { s32 hash_layer_count; Region hash_layer_region[HashLayerCountMax]; } hierarchical_sha256_data; - static_assert(std::is_pod<HierarchicalSha256Data>::value); + static_assert(util::is_pod<HierarchicalSha256Data>::value); struct IntegrityMetaInfo { static const size_t MasterHashOffset; @@ -249,7 +249,7 @@ namespace ams::fssystem { Hash master_hash; } integrity_meta_info; - static_assert(std::is_pod<IntegrityMetaInfo>::value); + static_assert(util::is_pod<IntegrityMetaInfo>::value); u8 padding[NcaPatchInfo::Offset - HashDataOffset]; }; @@ -266,7 +266,7 @@ namespace ams::fssystem { u8 pad[0x88]; }; static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size); - static_assert(std::is_pod<NcaFsHeader>::value); + static_assert(util::is_pod<NcaFsHeader>::value); static_assert(offsetof(NcaFsHeader, patch_info) == NcaPatchInfo::Offset); inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = offsetof(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp index e708fb8a9..b93ca0241 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_block_cache_buffered_storage.hpp @@ -32,7 +32,7 @@ namespace ams::fssystem::save { struct FileSystemBufferManagerSet { IBufferManager *buffers[IntegrityMaxLayerCount]; }; - static_assert(std::is_pod<FileSystemBufferManagerSet>::value); + static_assert(util::is_pod<FileSystemBufferManagerSet>::value); class BlockCacheBufferedStorage : public ::ams::fs::IStorage { NON_COPYABLE(BlockCacheBufferedStorage); @@ -54,7 +54,7 @@ namespace ams::fssystem::save { uintptr_t memory_address; size_t memory_size; }; - static_assert(std::is_pod<CacheEntry>::value); + static_assert(util::is_pod<CacheEntry>::value); enum Flag : s32 { Flag_KeepBurstMode = (1 << 8), diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp index 1140e3641..1283a2ac6 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp @@ -31,7 +31,7 @@ namespace ams::fssystem::save { s32 block_order; u8 reserved[4]; }; - static_assert(std::is_pod<HierarchicalIntegrityVerificationLevelInformation>::value); + static_assert(util::is_pod<HierarchicalIntegrityVerificationLevelInformation>::value); static_assert(sizeof(HierarchicalIntegrityVerificationLevelInformation) == 0x18); static_assert(alignof(HierarchicalIntegrityVerificationLevelInformation) == 0x4); @@ -52,7 +52,7 @@ namespace ams::fssystem::save { return this->info[this->max_layers - 2].size; } }; - static_assert(std::is_pod<HierarchicalIntegrityVerificationInformation>::value); + static_assert(util::is_pod<HierarchicalIntegrityVerificationInformation>::value); struct HierarchicalIntegrityVerificationMetaInformation { u32 magic; @@ -62,14 +62,14 @@ namespace ams::fssystem::save { /* TODO: Format */ }; - static_assert(std::is_pod<HierarchicalIntegrityVerificationMetaInformation>::value); + static_assert(util::is_pod<HierarchicalIntegrityVerificationMetaInformation>::value); struct HierarchicalIntegrityVerificationSizeSet { s64 control_size; s64 master_hash_size; s64 layered_hash_sizes[IntegrityMaxLayerCount - 1]; }; - static_assert(std::is_pod<HierarchicalIntegrityVerificationSizeSet>::value); + static_assert(util::is_pod<HierarchicalIntegrityVerificationSizeSet>::value); class HierarchicalIntegrityVerificationStorageControlArea { NON_COPYABLE(HierarchicalIntegrityVerificationStorageControlArea); @@ -80,7 +80,7 @@ namespace ams::fssystem::save { struct InputParam { size_t level_block_size[IntegrityMaxLayerCount - 1]; }; - static_assert(std::is_pod<InputParam>::value); + static_assert(util::is_pod<InputParam>::value); private: fs::SubStorage storage; HierarchicalIntegrityVerificationMetaInformation meta; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp index 13871b9fc..fbd9e8ae1 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp @@ -35,7 +35,7 @@ namespace ams::fssystem::save { struct BlockHash { u8 hash[HashSize]; }; - static_assert(std::is_pod<BlockHash>::value); + static_assert(util::is_pod<BlockHash>::value); private: fs::SubStorage hash_storage; fs::SubStorage data_storage; diff --git a/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp index a8f8046e6..2927777d3 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp @@ -21,7 +21,7 @@ namespace ams::fssystem { using Node = impl::BucketTreeNode<const s64 *>; static_assert(sizeof(Node) == sizeof(BucketTree::NodeHeader)); - static_assert(std::is_pod<Node>::value); + static_assert(util::is_pod<Node>::value); constexpr inline s32 NodeHeaderSize = sizeof(BucketTree::NodeHeader); diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp index d776fdba9..c8e1354b1 100644 --- a/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_buffered_storage.cpp @@ -31,7 +31,7 @@ namespace ams::fssystem::save { void *buffer; size_t size; }; - static_assert(std::is_pod<FetchParameter>::value); + static_assert(util::is_pod<FetchParameter>::value); private: BufferedStorage *buffered_storage; std::pair<uintptr_t, size_t> memory_range; From c8e5461e3f38dacb7d5b71aea22fd9e038591251 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 14 May 2020 02:22:58 -0700 Subject: [PATCH 029/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "bbbe6793" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "bbbe6793" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index aafd0bc01..b83f0a0b2 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = b38939adb58c8a28eb9556aa7abbcbed4d823c43 - parent = 81f91803ecde50a5007729ff36c7ee2b2357b4bf + commit = bbbe67937ae515a577c131608626b37c77af57a7 + parent = 53a47e07fd4b3270f11c6eaec5cff0f4865cf085 method = merge cmdver = 0.4.1 From 9598da0a0b966e93a6dd3ff61968a625b510b039 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Fri, 15 May 2020 17:28:51 -0400 Subject: [PATCH 030/118] Tweak default settings to allow nro replacement mods for smash (#956) --- config_templates/system_settings.ini | 2 +- stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index 32cebc036..74027f711 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -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. diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 79f7374a9..597cc02dd 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -313,7 +313,7 @@ namespace ams::settings::fwdbg { /* Control whether RO should ease its validation of NROs. */ /* (note: this is normally not necessary, and ips patches can be used.) */ - R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x0")); + R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x1")); /* Atmosphere custom settings. */ From 9baf096a106726ab46beb96382e19b8a16a74a2f Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 15:05:36 -0700 Subject: [PATCH 031/118] dmnt: make debug event result handling more robust (closes #938) --- .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 37 +++++++---- .../impl/dmnt_cheat_debug_events_manager.cpp | 66 +++++++++++-------- .../impl/dmnt_cheat_debug_events_manager.hpp | 2 +- stratosphere/dmnt/source/dmnt_main.cpp | 2 +- 4 files changed, 64 insertions(+), 43 deletions(-) diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 6369937ef..ee88402c4 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -599,25 +599,34 @@ namespace ams::dmnt::cheat::impl { /* Atomically wait (and clear) signal for new process. */ this_ptr->debug_events_event.Wait(); while (true) { - while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) { + Handle cheat_process_handle = this_ptr->GetCheatProcessHandle(); + while (cheat_process_handle != svc::InvalidHandle && R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) { this_ptr->cheat_lock.Lock(); ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); }; + { + ON_SCOPE_EXIT { cheat_process_handle = this_ptr->GetCheatProcessHandle(); }; - /* If we did an unsafe break, wait until we're not broken. */ - if (this_ptr->broken_unsafe) { - this_ptr->cheat_lock.Unlock(); - this_ptr->unsafe_break_event.Wait(); - this_ptr->cheat_lock.Lock(); - if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) { - continue; - } else { - break; + /* If we did an unsafe break, wait until we're not broken. */ + if (this_ptr->broken_unsafe) { + this_ptr->cheat_lock.Unlock(); + this_ptr->unsafe_break_event.Wait(); + this_ptr->cheat_lock.Lock(); + if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) { + continue; + } else { + break; + } } - } - /* Handle any pending debug events. */ - if (this_ptr->HasActiveCheatProcess()) { - dmnt::cheat::impl::ContinueCheatProcess(this_ptr->GetCheatProcessHandle()); + /* Handle any pending debug events. */ + if (this_ptr->HasActiveCheatProcess()) { + R_TRY_CATCH(dmnt::cheat::impl::ContinueCheatProcess(this_ptr->GetCheatProcessHandle())) { + R_CATCH(svc::ResultProcessTerminated) { + this_ptr->CloseActiveCheatProcess(); + break; + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } } } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 3cc7dd057..6a4ba7a39 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -27,10 +27,11 @@ namespace ams::dmnt::cheat::impl { static constexpr size_t NumCores = 4; static constexpr size_t ThreadStackSize = os::MemoryPageSize; private: - std::array<uintptr_t, NumCores> message_queue_buffers; - std::array<os::MessageQueue, NumCores> message_queues; + std::array<uintptr_t, NumCores> handle_message_queue_buffers; + std::array<uintptr_t, NumCores> result_message_queue_buffers; + std::array<os::MessageQueue, NumCores> handle_message_queues; + std::array<os::MessageQueue, NumCores> result_message_queues; std::array<os::ThreadType, NumCores> threads; - os::Event continued_event; alignas(os::MemoryPageSize) u8 thread_stacks[NumCores][ThreadStackSize]; private: @@ -43,14 +44,14 @@ namespace ams::dmnt::cheat::impl { Handle debug_handle = this_ptr->WaitReceiveHandle(current_core); /* Continue events on the correct core. */ - R_ABORT_UNLESS(this_ptr->ContinueDebugEvent(debug_handle)); + Result result = this_ptr->ContinueDebugEvent(debug_handle); - /* Signal that we've continued. */ - this_ptr->SignalContinued(); + /* Return our result. */ + this_ptr->SendContinueResult(current_core, result); } } - size_t GetTargetCore(const svc::DebugEventInfo &dbg_event, Handle debug_handle) { + Result GetTargetCore(size_t *out, const svc::DebugEventInfo &dbg_event, Handle debug_handle) { /* If we don't need to continue on a specific core, use the system core. */ size_t target_core = NumCores - 1; @@ -58,20 +59,26 @@ namespace ams::dmnt::cheat::impl { if (dbg_event.type == svc::DebugEvent_AttachThread) { u64 out64 = 0; u32 out32 = 0; - R_ABORT_UNLESS(svcGetDebugThreadParam(&out64, &out32, debug_handle, dbg_event.info.attach_thread.thread_id, DebugThreadParam_CurrentCore)); + + R_TRY_CATCH(svcGetDebugThreadParam(&out64, &out32, debug_handle, dbg_event.info.attach_thread.thread_id, DebugThreadParam_CurrentCore)) { + R_CATCH_RETHROW(svc::ResultProcessTerminated) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + target_core = out32; } - return target_core; + /* Set the target core. */ + *out = target_core; + return ResultSuccess(); } void SendHandle(size_t target_core, Handle debug_handle) { - this->message_queues[target_core].Send(static_cast<uintptr_t>(debug_handle)); + this->handle_message_queues[target_core].Send(static_cast<uintptr_t>(debug_handle)); } Handle WaitReceiveHandle(size_t core_id) { uintptr_t x = 0; - this->message_queues[core_id].Receive(&x); + this->handle_message_queues[core_id].Receive(&x); return static_cast<Handle>(x); } @@ -83,22 +90,27 @@ namespace ams::dmnt::cheat::impl { } } - void WaitContinued() { - this->continued_event.Wait(); + void SendContinueResult(size_t target_core, Result result) { + this->result_message_queues[target_core].Send(static_cast<uintptr_t>(result.GetValue())); } - void SignalContinued() { - this->continued_event.Signal(); + Result GetContinueResult(size_t core_id) { + uintptr_t x = 0; + this->result_message_queues[core_id].Receive(&x); + return static_cast<Result>(x); } - public: DebugEventsManager() - : message_queues{ - os::MessageQueue(std::addressof(message_queue_buffers[0]), 1), - os::MessageQueue(std::addressof(message_queue_buffers[1]), 1), - os::MessageQueue(std::addressof(message_queue_buffers[2]), 1), - os::MessageQueue(std::addressof(message_queue_buffers[3]), 1)}, - continued_event(os::EventClearMode_AutoClear), + : handle_message_queues{ + os::MessageQueue(std::addressof(handle_message_queue_buffers[0]), 1), + os::MessageQueue(std::addressof(handle_message_queue_buffers[1]), 1), + os::MessageQueue(std::addressof(handle_message_queue_buffers[2]), 1), + os::MessageQueue(std::addressof(handle_message_queue_buffers[3]), 1)}, + result_message_queues{ + os::MessageQueue(std::addressof(result_message_queue_buffers[0]), 1), + os::MessageQueue(std::addressof(result_message_queue_buffers[1]), 1), + os::MessageQueue(std::addressof(result_message_queue_buffers[2]), 1), + os::MessageQueue(std::addressof(result_message_queue_buffers[3]), 1)}, thread_stacks{} { for (size_t i = 0; i < NumCores; i++) { @@ -114,19 +126,19 @@ namespace ams::dmnt::cheat::impl { } } - void ContinueCheatProcess(Handle cheat_dbg_hnd) { + Result ContinueCheatProcess(Handle cheat_dbg_hnd) { /* Loop getting all debug events. */ svc::DebugEventInfo d; size_t target_core = NumCores - 1; while (R_SUCCEEDED(svc::GetDebugEvent(std::addressof(d), cheat_dbg_hnd))) { if (d.type == svc::DebugEvent_AttachThread) { - target_core = GetTargetCore(d, cheat_dbg_hnd); + R_TRY(GetTargetCore(std::addressof(target_core), d, cheat_dbg_hnd)); } } /* Send handle to correct core, wait for continue to finish. */ this->SendHandle(target_core, cheat_dbg_hnd); - this->WaitContinued(); + return this->GetContinueResult(target_core); } }; @@ -139,8 +151,8 @@ namespace ams::dmnt::cheat::impl { new (GetPointer(g_events_manager)) DebugEventsManager; } - void ContinueCheatProcess(Handle cheat_dbg_hnd) { - GetReference(g_events_manager).ContinueCheatProcess(cheat_dbg_hnd); + Result ContinueCheatProcess(Handle cheat_dbg_hnd) { + return GetReference(g_events_manager).ContinueCheatProcess(cheat_dbg_hnd); } } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp index 604183c60..f5eea0ea0 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.hpp @@ -20,6 +20,6 @@ namespace ams::dmnt::cheat::impl { void InitializeDebugEventsManager(); - void ContinueCheatProcess(Handle cheat_dbg_hnd); + Result ContinueCheatProcess(Handle cheat_dbg_hnd); } diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 12aded756..3839d4b9a 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -40,7 +40,7 @@ namespace ams { namespace result { - bool CallFatalOnResultAssertion = true; + bool CallFatalOnResultAssertion = false; } From 8052dd62493819708d6b2e6c895214c0169af26e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 16:00:45 -0700 Subject: [PATCH 032/118] fusee: fix boot support on < 7.x --- fusee/fusee-secondary/src/masterkey.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fusee/fusee-secondary/src/masterkey.c b/fusee/fusee-secondary/src/masterkey.c index 4cbcf42cf..430709fad 100644 --- a/fusee/fusee-secondary/src/masterkey.c +++ b/fusee/fusee-secondary/src/masterkey.c @@ -182,8 +182,6 @@ void derive_new_device_keys(bool is_retail, unsigned int keygen_keyslot, unsigne set_old_devkey(relative_revision, work_buffer); } } - set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); - clear_aes_keyslot(keygen_keyslot); } void set_old_devkey(unsigned int revision, const uint8_t *key) { From 19d8a0fc2bb1aba5f4fd97b1a10fd8d156d12b98 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 17 May 2020 23:20:21 -0700 Subject: [PATCH 033/118] os: use ported libnx mutex impl --- ...ernal_critical_section_impl.os.horizon.cpp | 148 +++--------------- 1 file changed, 24 insertions(+), 124 deletions(-) diff --git a/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp index effa13f5f..48a21f96d 100644 --- a/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_internal_critical_section_impl.os.horizon.cpp @@ -19,142 +19,42 @@ namespace ams::os::impl { - namespace { + #if defined(ATMOSPHERE_ARCH_ARM64) - ALWAYS_INLINE void DataMemoryBarrierForCriticalSection() { - #if defined(ATMOSPHERE_ARCH_ARM64) - /* ... */ - #else - #error "Unknown architecture for os::impl::InternalCriticalSectionImpl DataMemoryBarrier" - #endif + void InternalCriticalSectionImpl::Enter() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); + + /* Use the libnx impl. */ + static_assert(std::is_same<decltype(this->thread_handle), ::Mutex>::value); + return ::mutexLock(std::addressof(this->thread_handle)); } - ALWAYS_INLINE u32 LoadExclusive(u32 *ptr) { - u32 value; + bool InternalCriticalSectionImpl::TryEnter() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); - #if defined(ATMOSPHERE_ARCH_ARM64) - __asm__ __volatile__("ldaxr %w[value], [%[ptr]]" : [value]"=&r"(value) : [ptr]"r"(ptr) : "memory"); - #else - #error "Unknown architecture for os::impl::InternalCriticalSectionImpl LoadExclusive" - #endif - - return value; + /* Use the libnx impl. */ + static_assert(std::is_same<decltype(this->thread_handle), ::Mutex>::value); + return ::mutexTryLock(std::addressof(this->thread_handle)); } - ALWAYS_INLINE int StoreExclusive(u32 *ptr, u32 value) { - int result; + void InternalCriticalSectionImpl::Leave() { + AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); - #if defined(ATMOSPHERE_ARCH_ARM64) - __asm__ __volatile__("stlxr %w[result], %w[value], [%[ptr]]" : [result]"=&r"(result) : [value]"r"(value), [ptr]"r"(ptr) : "memory"); - #else - #error "Unknown architecture for os::impl::InternalCriticalSectionImpl StoreExclusive" - #endif - - - return result; + /* Use the libnx impl. */ + static_assert(std::is_same<decltype(this->thread_handle), ::Mutex>::value); + return ::mutexUnlock(std::addressof(this->thread_handle)); } - ALWAYS_INLINE void ClearExclusive() { - #if defined(ATMOSPHERE_ARCH_ARM64) - __asm__ __volatile__("clrex" ::: "memory"); - #else - #error "Unknown architecture for os::impl::InternalCriticalSectionImpl ClearExclusive" - #endif + bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const { + /* Use the libnx impl. */ + static_assert(std::is_same<decltype(this->thread_handle), ::Mutex>::value); + return ::mutexIsLockedByCurrentThread(std::addressof(this->thread_handle)); } - } + #else - void InternalCriticalSectionImpl::Enter() { - AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); + #error "Architecture not yet supported for os::InternalCriticalSectionImpl" - const auto cur_handle = GetCurrentThreadHandle(); - AMS_ASSERT((this->thread_handle & ~ams::svc::HandleWaitMask) != cur_handle); - - u32 value = LoadExclusive(std::addressof(this->thread_handle)); - while (true) { - if (AMS_LIKELY(value == svc::InvalidHandle)) { - if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) != 0)) { - value = LoadExclusive(std::addressof(this->thread_handle)); - continue; - } - break; - } - - if (AMS_LIKELY((value & ams::svc::HandleWaitMask) == 0)) { - if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), value | ams::svc::HandleWaitMask) != 0)) { - value = LoadExclusive(std::addressof(this->thread_handle)); - continue; - } - } - - R_ABORT_UNLESS(ams::svc::ArbitrateLock(value & ~ams::svc::HandleWaitMask, reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle)), cur_handle)); - - value = LoadExclusive(std::addressof(this->thread_handle)); - if (AMS_LIKELY((value & ~ams::svc::HandleWaitMask) == cur_handle)) { - ClearExclusive(); - break; - } - } - - DataMemoryBarrierForCriticalSection(); - } - - bool InternalCriticalSectionImpl::TryEnter() { - AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); - - const auto cur_handle = GetCurrentThreadHandle(); - - while (true) { - u32 value = LoadExclusive(std::addressof(this->thread_handle)); - if (AMS_UNLIKELY(value != svc::InvalidHandle)) { - break; - } - - DataMemoryBarrierForCriticalSection(); - - if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) == 0)) { - return true; - } - } - - ClearExclusive(); - DataMemoryBarrierForCriticalSection(); - - return false; - } - - void InternalCriticalSectionImpl::Leave() { - AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0); - - const auto cur_handle = GetCurrentThreadHandle(); - u32 value = LoadExclusive(std::addressof(this->thread_handle)); - - while (true) { - if (AMS_UNLIKELY(value != cur_handle)) { - ClearExclusive(); - break; - } - - DataMemoryBarrierForCriticalSection(); - - if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), 0) == 0)) { - break; - } - - value = LoadExclusive(std::addressof(this->thread_handle)); - } - - DataMemoryBarrierForCriticalSection(); - - AMS_ASSERT((value | ams::svc::HandleWaitMask) == (cur_handle | ams::svc::HandleWaitMask)); - if (value & ams::svc::HandleWaitMask) { - R_ABORT_UNLESS(ams::svc::ArbitrateUnlock(reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle)))); - } - } - - bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const { - const auto cur_handle = GetCurrentThreadHandle(); - return (this->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle; - } + #endif } From 79ae47f028ef9994fcd9c390d7523ceb37ad608c Mon Sep 17 00:00:00 2001 From: Adubbz <Adubbz@users.noreply.github.com> Date: Tue, 19 May 2020 01:03:38 +1000 Subject: [PATCH 034/118] ncm: implement firmware downgrading (#958) * ncm: implement firmware downgrading * ncm: make storage list const --- .../include/stratosphere/ncm.hpp | 1 + .../ncm/ncm_install_task_base.hpp | 4 +- .../ncm/ncm_package_system_downgrade_task.hpp | 30 ++++++++ .../ncm/ncm_package_system_update_task.hpp | 3 +- .../ncm/ncm_package_system_downgrade_task.cpp | 76 +++++++++++++++++++ 5 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp create mode 100644 libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index e87fdfa2c..04cb7515f 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -36,6 +36,7 @@ #include <stratosphere/ncm/ncm_memory_report.hpp> #include <stratosphere/ncm/ncm_package_install_task_base.hpp> #include <stratosphere/ncm/ncm_package_install_task.hpp> +#include <stratosphere/ncm/ncm_package_system_downgrade_task.hpp> #include <stratosphere/ncm/ncm_package_system_update_task.hpp> #include <stratosphere/ncm/ncm_submission_package_install_task.hpp> #include <stratosphere/ncm/ncm_storage_utils.hpp> diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp index 4959f42fe..8243efbb2 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_install_task_base.hpp @@ -139,10 +139,11 @@ namespace ams::ncm { Result CountInstallContentMetaData(s32 *out_count); Result GetInstallContentMetaData(InstallContentMeta *out_content_meta, s32 index); Result DeleteInstallContentMetaData(const ContentMetaKey *keys, s32 num_keys); + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) = 0; virtual Result PrepareDependency(); Result PrepareSystemUpdateDependency(); - Result PrepareContentMetaIfLatest(const ContentMetaKey &key); + virtual Result PrepareContentMetaIfLatest(const ContentMetaKey &key); /* NOTE: This is not virtual in Nintendo's code. We do so to facilitate downgrades. */ u32 GetConfig() const { return this->config; } Result WriteContentMetaToPlaceHolder(InstallContentInfo *out_install_content_info, ContentStorage *storage, const InstallContentMetaInfo &meta_info, std::optional<bool> is_temporary); @@ -163,7 +164,6 @@ namespace ams::ncm { Result VerifyAllNotCommitted(const StorageContentMetaKey *keys, s32 num_keys); virtual Result PrepareInstallContentMetaData() = 0; - virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out_info, const ContentMetaKey &key) = 0; virtual Result GetLatestVersion(std::optional<u32> *out_version, u64 id) { return ncm::ResultContentMetaNotFound(); } virtual Result OnExecuteComplete() { return ResultSuccess(); } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp new file mode 100644 index 000000000..e70af190d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_downgrade_task.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#pragma once +#include <stratosphere/ncm/ncm_package_system_update_task.hpp> + +namespace ams::ncm { + + class PackageSystemDowngradeTask : public PackageSystemUpdateTask { + public: + Result Commit(); + protected: + virtual Result PrepareContentMetaIfLatest(const ContentMetaKey &key) override; + private: + Result PreCommit(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp index 4648d20f6..fdaf68217 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_package_system_update_task.hpp @@ -34,9 +34,10 @@ namespace ams::ncm { std::optional<ContentMetaKey> GetSystemUpdateMetaKey(); protected: virtual Result PrepareInstallContentMetaData() override; + virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; + InstallTaskDataBase &GetInstallData() { return this->data; } /* Atmosphere extension. */ private: virtual Result PrepareDependency() override; - virtual Result GetInstallContentMetaInfo(InstallContentMetaInfo *out, const ContentMetaKey &key) override; Result GetContentInfoOfContentMeta(ContentInfo *out, const ContentMetaKey &key); }; diff --git a/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp b/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp new file mode 100644 index 000000000..95b7a518d --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_package_system_downgrade_task.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stratosphere.hpp> + +namespace ams::ncm { + + Result PackageSystemDowngradeTask::PreCommit() { + constexpr size_t MaxContentMetas = 0x40; + + auto &data = this->GetInstallData(); + + /* Count the number of content meta entries. */ + s32 count; + R_TRY(data.Count(std::addressof(count))); + + /* Iterate over content meta. */ + for (s32 i = 0; i < count; i++) { + /* Obtain the content meta. */ + InstallContentMeta content_meta; + R_TRY(data.Get(std::addressof(content_meta), i)); + + /* Create a reader. */ + const auto reader = content_meta.GetReader(); + const auto key = reader.GetKey(); + + /* Obtain a list of suitable storage ids. */ + const auto storage_list = GetStorageList(this->GetInstallStorage()); + + /* Iterate over storage ids. */ + for (s32 i = 0; i < storage_list.Count(); i++) { + /* Open the content meta database. */ + ContentMetaDatabase meta_db; + if (R_FAILED(OpenContentMetaDatabase(std::addressof(meta_db), storage_list[i]))) { + continue; + } + + /* List keys matching the type and id of the install content meta key. */ + ncm::ContentMetaKey keys[MaxContentMetas]; + const auto count = meta_db.ListContentMeta(keys, MaxContentMetas, key.type, { key.id }); + + /* Remove matching keys. */ + for (auto i = 0; i < count.total; i++) { + meta_db.Remove(keys[i]); + } + } + } + + return ResultSuccess(); + } + + Result PackageSystemDowngradeTask::Commit() { + R_TRY(this->PreCommit()); + return InstallTaskBase::Commit(); + } + + Result PackageSystemDowngradeTask::PrepareContentMetaIfLatest(const ContentMetaKey &key) { + /* Get and prepare install content meta info. We aren't concerned if our key is older. */ + InstallContentMetaInfo install_content_meta_info; + R_TRY(this->GetInstallContentMetaInfo(std::addressof(install_content_meta_info), key)); + return this->PrepareContentMeta(install_content_meta_info, key, std::nullopt); + } + +} From de9a3c6dfcd91cc822280595284067acf9141e5b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 18 May 2020 08:22:36 -0700 Subject: [PATCH 035/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "797dfa78" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "797dfa78" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index b83f0a0b2..237468473 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = bbbe67937ae515a577c131608626b37c77af57a7 - parent = 53a47e07fd4b3270f11c6eaec5cff0f4865cf085 + commit = 797dfa782e85173652d017de38066f9a5c88622a + parent = 79ae47f028ef9994fcd9c390d7523ceb37ad608c method = merge cmdver = 0.4.1 From a2496e546287d42fc4a4b74b89ea4bcffc787599 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 18 May 2020 08:57:20 -0700 Subject: [PATCH 036/118] exo: fix warmboot memory address error --- exosphere/src/package2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index 32bb76ad8..bee6a60f0 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -442,7 +442,7 @@ static void copy_warmboot_bin_to_dram() { const uint32_t target_fw = exosphere_get_target_firmware(); if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { warmboot_src = (uint8_t *)0x4003E000; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { + } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { warmboot_src = (uint8_t *)0x4003D800; } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { warmboot_src = (uint8_t *)0x4003B000; From f215da3b3731ad6c0d27a600a86772a5bf1fd78d Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 19 May 2020 09:48:44 -0700 Subject: [PATCH 037/118] fs: miscellaneous bucket tree fixes --- .../stratosphere/fssystem/fssystem_bucket_tree.hpp | 4 ++-- .../source/fssystem/fssystem_bucket_tree.cpp | 12 ++++++------ .../include/vapours/results/fs_results.hpp | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp index fdf718b4d..370de5969 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_bucket_tree.hpp @@ -126,7 +126,7 @@ namespace ams::fssystem { this->allocator = nullptr; } - void FillSzero(size_t node_size) const { + void FillZero(size_t node_size) const { if (this->header) { std::memset(this->header, 0, node_size); } @@ -298,7 +298,7 @@ namespace ams::fssystem { } bool IsValid() const { return this->entry_index >= 0; } - bool CanMoveNext() const { return this->IsValid() && (this->entry_index + 1 < this->entry_set.info.count || this->entry_set.info.index + 1 < this->entry_set.info.count); } + bool CanMoveNext() const { return this->IsValid() && (this->entry_index + 1 < this->entry_set.info.count || this->entry_set.info.index + 1 < this->entry_set_count); } bool CanMovePrevious() const { return this->IsValid() && (this->entry_index > 0 || this->entry_set.info.index > 0); } Result MoveNext(); diff --git a/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp index 2927777d3..9888bf237 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_bucket_tree.cpp @@ -130,12 +130,12 @@ namespace ams::fssystem { } Result BucketTree::NodeHeader::Verify(s32 node_index, size_t node_size, size_t entry_size) const { - R_UNLESS(this->index == node_index, fs::ResultInvalidArgument()); - R_UNLESS(entry_size == 0 || node_size < entry_size + NodeHeaderSize, fs::ResultInvalidSize()); + R_UNLESS(this->index == node_index, fs::ResultInvalidBucketTreeNodeIndex()); + R_UNLESS(entry_size != 0 && node_size >= entry_size + NodeHeaderSize, fs::ResultInvalidSize()); const size_t max_entry_count = (node_size - NodeHeaderSize) / entry_size; R_UNLESS(this->count > 0 && static_cast<size_t>(this->count) <= max_entry_count, fs::ResultInvalidBucketTreeNodeEntryCount()); - R_UNLESS(this->offset > 0, fs::ResultInvalidBucketTreeNodeOffset()); + R_UNLESS(this->offset >= 0, fs::ResultInvalidBucketTreeNodeOffset()); return ResultSuccess(); } @@ -440,7 +440,7 @@ namespace ams::fssystem { /* Create the node, and find. */ StorageNode node(sizeof(s64), header.count); node.Find(buffer, virtual_address); - R_UNLESS(node.GetIndex() >= 0, fs::ResultOutOfRange()); + R_UNLESS(node.GetIndex() >= 0, fs::ResultInvalidBucketTreeVirtualOffset()); /* Return the index. */ *out_index = this->tree->GetEntrySetIndex(header.index, node.GetIndex()); @@ -485,7 +485,7 @@ namespace ams::fssystem { const auto entry_size = this->tree->entry_size; const auto entry_set_size = this->tree->node_size; const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); - fs::SubStorage &storage = tree->node_storage; + fs::SubStorage &storage = tree->entry_storage; /* Read the entry set. */ R_TRY(storage.Read(entry_set_offset, buffer, entry_set_size)); @@ -517,7 +517,7 @@ namespace ams::fssystem { const auto entry_size = this->tree->entry_size; const auto entry_set_size = this->tree->node_size; const auto entry_set_offset = entry_set_index * static_cast<s64>(entry_set_size); - fs::SubStorage &storage = tree->node_storage; + fs::SubStorage &storage = tree->entry_storage; /* Read and validate the entry_set. */ EntrySetHeader entry_set; diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index c4e9962ec..0eeb9ada7 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -141,6 +141,8 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(InvalidBucketTreeNodeOffset, 4035); R_DEFINE_ERROR_RESULT(InvalidBucketTreeEntryOffset, 4036); R_DEFINE_ERROR_RESULT(InvalidBucketTreeEntrySetOffset, 4037); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeNodeIndex, 4038); + R_DEFINE_ERROR_RESULT(InvalidBucketTreeVirtualOffset, 4039); R_DEFINE_ERROR_RANGE(RomNcaCorrupted, 4041, 4139); R_DEFINE_ERROR_RANGE(RomNcaFileSystemCorrupted, 4051, 4069); From 80e49696ea4adf5d9aedb7deee84136ba7ebe498 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 25 May 2020 19:33:21 -0700 Subject: [PATCH 038/118] ams: update for 10.0.3 --- fusee/fusee-secondary/src/nxboot.c | 1 + libraries/libvapours/include/vapours/ams/ams_api_version.h | 2 +- .../libvapours/include/vapours/ams/ams_target_firmware.h | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index d82b9b1eb..1a50ba754 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -236,6 +236,7 @@ static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + CHECK_NCA("5b1df84f88c3334335bbb45d8522cbb4", 10_0_3); CHECK_NCA("e951bc9dedcd54f65ffd83d4d050f9e0", 10_0_2); CHECK_NCA("36ab1acf0c10a2beb9f7d472685f9a89", 10_0_1); CHECK_NCA("5625cdc21d5f1ca52f6c36ba261505b9", 10_0_0); diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index ac177235b..15e56be44 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -23,4 +23,4 @@ #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 10 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 3 diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index 1d51bbe61..75ab4cedc 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -51,8 +51,9 @@ #define ATMOSPHERE_TARGET_FIRMWARE_10_0_0 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_10_0_1 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 1) #define ATMOSPHERE_TARGET_FIRMWARE_10_0_2 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 2) +#define ATMOSPHERE_TARGET_FIRMWARE_10_0_3 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 3) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_0_2 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_0_3 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT @@ -96,6 +97,7 @@ namespace ams { TargetFirmware_10_0_0 = ATMOSPHERE_TARGET_FIRMWARE_10_0_0, TargetFirmware_10_0_1 = ATMOSPHERE_TARGET_FIRMWARE_10_0_1, TargetFirmware_10_0_2 = ATMOSPHERE_TARGET_FIRMWARE_10_0_2, + TargetFirmware_10_0_3 = ATMOSPHERE_TARGET_FIRMWARE_10_0_3, TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT, From fe0bd03febab7f39efc51bf3e984f6771cd78896 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 27 May 2020 03:51:11 -0700 Subject: [PATCH 039/118] strat: use 1 fewer fs session at runtime (and match official ncm usage) --- stratosphere/boot/source/boot_main.cpp | 1 + stratosphere/ncm/source/ncm_main.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 50a7b7a2e..07e6edc3c 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -35,6 +35,7 @@ extern "C" { extern u32 __start__; u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; /* TODO: Evaluate to what extent this can be reduced further. */ #define INNER_HEAP_SIZE 0x20000 diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index 8d16556c1..d9ed098ea 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -19,6 +19,7 @@ extern "C" { extern u32 __start__; u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 2; #define INNER_HEAP_SIZE 0x8000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; From 71e0102f7a877a30c38d13d12d4d32a212bd4d76 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 27 May 2020 03:58:10 -0700 Subject: [PATCH 040/118] boot2: correct service access for mitm-forward-declare --- stratosphere/boot2/boot2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/boot2/boot2.json b/stratosphere/boot2/boot2.json index 125c538f3..89f48328c 100644 --- a/stratosphere/boot2/boot2.json +++ b/stratosphere/boot2/boot2.json @@ -14,7 +14,7 @@ "filesystem_access": { "permissions": "0xFFFFFFFFFFFFFFFF" }, - "service_host": [], + "service_host": ["*"], "service_access": ["fsp-srv", "gpio", "htc", "lr", "pm:bm", "pm:shell", "pm:info", "set:sys"], "kernel_capabilities": [ { From f66b41c0278e50d622d6aeeff1f0667905a3d984 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 4 May 2020 23:33:16 -0700 Subject: [PATCH 041/118] exo2: Initial work on the exosphere rewrite. exo2: Implement uncompressor stub and boot code up to Main(). exo2: implement some more init (uart/gic) exo2: implement more of init exo2: improve reg api, add keyslot flag setters exo2: implement se aes decryption/enc exo2: fix bugs in loader stub/mmu mappings exo2: start skeletoning bootconfig/global context types arch: fix makefile flags exo2: implement through master key derivation exo2: implement device master keygen exo2: more init through start of SetupSocSecurity exo2: implement pmc secure scratch management se: implement sticky bit validation libexosphere: fix building for arm32 libexo: fix makefile flags libexo: support building for arm64/arm sc7fw: skeleton binary sc7fw: skeleton a little more sc7fw: implement all non-dram functionality exo2: fix DivideUp error sc7fw: implement more dram code, fix reg library errors sc7fw: complete sc7fw impl. exo2: skeleton the rest of SetupSocSecurity exo2: implement fiq interrupt handler exo2: implement all exception handlers exo2: skeleton the entire smc api, implement the svc invoker exo2: implement rest of SetupSocSecurity exo2: correct slave security errors exo2: fix register definition exo2: minor fixes --- .gitignore | 1 + exosphere/src/start.s | 8 +- exosphere2/Makefile | 38 + exosphere2/loader_stub/Makefile | 130 ++ exosphere2/loader_stub/loader_stub.ld | 182 +++ exosphere2/loader_stub/loader_stub.specs | 7 + .../source/secmon_loader_error.cpp | 47 + .../source/secmon_loader_error.hpp | 23 + .../loader_stub/source/secmon_loader_main.cpp | 41 + .../source/secmon_loader_uncompress.cpp | 103 ++ .../source/secmon_loader_uncompress.hpp | 23 + exosphere2/loader_stub/source/start.s | 105 ++ exosphere2/program/Makefile | 128 ++ exosphere2/program/program.ld | 277 ++++ exosphere2/program/program.specs | 4 + exosphere2/program/sc7fw/Makefile | 122 ++ exosphere2/program/sc7fw/sc7fw.ld | 183 +++ exosphere2/program/sc7fw/sc7fw.specs | 7 + .../program/sc7fw/source/sc7fw_dram.cpp | 182 +++ .../program/sc7fw/source/sc7fw_dram.hpp | 26 + .../sc7fw/source/sc7fw_exception_vectors.s | 22 + .../program/sc7fw/source/sc7fw_main.cpp | 116 ++ exosphere2/program/sc7fw/source/sc7fw_start.s | 35 + .../program/sc7fw/source/sc7fw_util.hpp | 23 + .../program/sc7fw/source/sc7fw_util_asm.s | 30 + .../program/source/boot/secmon_boot.hpp | 25 + .../program/source/boot/secmon_boot_cache.cpp | 22 + .../source/boot/secmon_boot_config.cpp | 22 + .../source/boot/secmon_boot_functions.cpp | 26 + .../source/boot/secmon_boot_functions.hpp | 23 + .../program/source/boot/secmon_boot_setup.cpp | 337 +++++ exosphere2/program/source/boot/secmon_crt0.s | 37 + .../program/source/boot/secmon_crt0_cpp.cpp | 46 + .../program/source/boot/secmon_key_data.cpp | 22 + .../program/source/boot/secmon_main.cpp | 69 + .../source/boot/secmon_make_page_table.cpp | 147 +++ .../program/source/boot/secmon_package2.cpp | 22 + .../program/source/boot/secmon_size_data.s | 26 + exosphere2/program/source/secmon_cache.inc | 22 + .../program/source/secmon_cache_impl.inc | 132 ++ .../program/source/secmon_cpu_context.cpp | 192 +++ .../program/source/secmon_cpu_context.hpp | 41 + exosphere2/program/source/secmon_error.cpp | 51 + exosphere2/program/source/secmon_error.hpp | 24 + .../program/source/secmon_exception_vectors.s | 326 +++++ .../source/secmon_interrupt_handler.cpp | 64 + .../source/secmon_interrupt_handler.hpp | 25 + .../program/source/secmon_key_storage.cpp | 103 ++ .../program/source/secmon_key_storage.hpp | 44 + exosphere2/program/source/secmon_misc.cpp | 46 + exosphere2/program/source/secmon_misc.hpp | 29 + exosphere2/program/source/secmon_setup.cpp | 750 +++++++++++ exosphere2/program/source/secmon_setup.hpp | 36 + .../program/source/secmon_setup_warm.cpp | 206 +++ .../program/source/secmon_start_virtual.s | 83 ++ .../program/source/smc/secmon_smc_aes.cpp | 52 + .../program/source/smc/secmon_smc_aes.hpp | 29 + .../source/smc/secmon_smc_carveout.cpp | 27 + .../source/smc/secmon_smc_carveout.hpp | 24 + .../program/source/smc/secmon_smc_common.hpp | 45 + .../smc/secmon_smc_device_unique_data.cpp | 43 + .../smc/secmon_smc_device_unique_data.hpp | 29 + .../program/source/smc/secmon_smc_error.cpp | 27 + .../program/source/smc/secmon_smc_error.hpp | 24 + .../program/source/smc/secmon_smc_es.cpp | 32 + .../program/source/smc/secmon_smc_es.hpp | 32 + .../program/source/smc/secmon_smc_handler.cpp | 233 ++++ .../program/source/smc/secmon_smc_handler.hpp | 24 + .../program/source/smc/secmon_smc_info.cpp | 43 + .../program/source/smc/secmon_smc_info.hpp | 59 + .../source/smc/secmon_smc_memory_access.cpp | 33 + .../source/smc/secmon_smc_memory_access.hpp | 26 + .../smc/secmon_smc_power_management.cpp | 37 + .../smc/secmon_smc_power_management.hpp | 27 + .../program/source/smc/secmon_smc_random.cpp | 32 + .../program/source/smc/secmon_smc_random.hpp | 25 + .../source/smc/secmon_smc_register_access.cpp | 27 + .../source/smc/secmon_smc_register_access.hpp | 24 + .../program/source/smc/secmon_smc_result.cpp | 32 + .../program/source/smc/secmon_smc_result.hpp | 31 + .../program/source/smc/secmon_smc_rsa.cpp | 32 + .../program/source/smc/secmon_smc_rsa.hpp | 25 + exosphere2/program/split_program.py | 33 + libraries/config/arch/arm/arch.mk | 11 + libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk | 5 + .../config/board/nintendo/nx_bpmp/board.mk | 5 + libraries/config/common.mk | 23 +- libraries/config/templates/exosphere.mk | 46 + libraries/libexosphere/Makefile | 19 + libraries/libexosphere/arm.mk | 142 ++ libraries/libexosphere/arm64.mk | 143 +++ libraries/libexosphere/include/exosphere.hpp | 38 + .../libexosphere/include/exosphere/actmon.hpp | 30 + .../libexosphere/include/exosphere/clkrst.hpp | 29 + .../libexosphere/include/exosphere/common.hpp | 30 + .../libexosphere/include/exosphere/fuse.hpp | 49 + .../libexosphere/include/exosphere/gic.hpp | 42 + .../libexosphere/include/exosphere/hw.hpp | 27 + .../include/exosphere/hw/hw_arm.hpp | 49 + .../include/exosphere/hw/hw_arm64.hpp | 69 + .../include/exosphere/hw/hw_arm64_cache.hpp | 55 + .../hw/hw_arm64_system_registers.hpp | 245 ++++ .../libexosphere/include/exosphere/i2c.hpp | 46 + .../libexosphere/include/exosphere/log.hpp | 23 + .../libexosphere/include/exosphere/mmu.hpp | 18 + .../exosphere/mmu/mmu_api.arch.arm.hpp | 43 + .../exosphere/mmu/mmu_api.arch.arm64.hpp | 279 ++++ .../include/exosphere/mmu/mmu_api.hpp | 37 + .../libexosphere/include/exosphere/pkg1.hpp | 22 + .../exosphere/pkg1/pkg1_boot_config.hpp | 133 ++ .../pkg1/pkg1_bootloader_parameters.hpp | 68 + .../exosphere/pkg1/pkg1_error_types.hpp | 108 ++ .../exosphere/pkg1/pkg1_key_generation.hpp | 47 + .../exosphere/pkg1/pkg1_se_key_slots.hpp | 56 + .../libexosphere/include/exosphere/pmc.hpp | 50 + .../libexosphere/include/exosphere/pmic.hpp | 33 + .../libexosphere/include/exosphere/reg.hpp | 205 +++ .../libexosphere/include/exosphere/se.hpp | 24 + .../include/exosphere/se/se_aes.hpp | 36 + .../include/exosphere/se/se_common.hpp | 53 + .../include/exosphere/se/se_management.hpp | 33 + .../include/exosphere/se/se_rng.hpp | 28 + .../include/exosphere/se/se_rsa.hpp | 29 + .../include/exosphere/se/se_suspend.hpp | 55 + .../libexosphere/include/exosphere/secmon.hpp | 20 + ...ecmon_configuration_context.arch.arm64.hpp | 99 ++ .../secmon/secmon_configuration_context.hpp | 25 + .../secmon/secmon_emummc_context.hpp | 73 ++ .../exosphere/secmon/secmon_memory_layout.hpp | 277 ++++ .../secmon/secmon_monitor_context.hpp | 77 ++ .../secmon/secmon_volatile_context.hpp | 62 + .../libexosphere/include/exosphere/tegra.hpp | 31 + .../exosphere/tegra/tegra_ahb_arbc.hpp | 26 + .../exosphere/tegra/tegra_apb_misc.hpp | 99 ++ .../exosphere/tegra/tegra_avp_cache.hpp | 35 + .../include/exosphere/tegra/tegra_emc.hpp | 90 ++ .../include/exosphere/tegra/tegra_evp.hpp | 19 + .../exosphere/tegra/tegra_flow_ctlr.hpp | 38 + .../include/exosphere/tegra/tegra_ictlr.hpp | 27 + .../include/exosphere/tegra/tegra_mc.hpp | 329 +++++ .../include/exosphere/tegra/tegra_mselect.hpp | 22 + .../include/exosphere/tegra/tegra_pmc.hpp | 156 +++ .../include/exosphere/tegra/tegra_sb.hpp | 41 + .../include/exosphere/tegra/tegra_sysctr0.hpp | 36 + .../include/exosphere/tegra/tegra_timer.hpp | 41 + .../libexosphere/include/exosphere/tsec.hpp | 23 + .../libexosphere/include/exosphere/uart.hpp | 42 + .../libexosphere/include/exosphere/util.hpp | 33 + .../libexosphere/include/exosphere/wdt.hpp | 24 + .../libexosphere/source/actmon/actmon_api.cpp | 42 + .../libexosphere/source/clkrst/clkrst_api.cpp | 98 ++ .../source/clkrst/clkrst_registers.hpp | 71 + .../libexosphere/source/fuse/fuse_api.cpp | 121 ++ .../source/fuse/fuse_registers.hpp | 221 ++++ libraries/libexosphere/source/gic/gic_api.cpp | 217 ++++ .../source/hw/hw_cache.arch.arm64.cpp | 29 + libraries/libexosphere/source/i2c/i2c_api.cpp | 202 +++ .../libexosphere/source/i2c/i2c_registers.hpp | 77 ++ libraries/libexosphere/source/libc/libc.c | 1141 +++++++++++++++++ libraries/libexosphere/source/log/log_api.cpp | 53 + libraries/libexosphere/source/pmc/pmc_api.cpp | 275 ++++ .../source/pmc/pmc_secure_scratch_test.inc | 100 ++ libraries/libexosphere/source/pmic/max77620.h | 340 +++++ libraries/libexosphere/source/pmic/max7762x.h | 109 ++ .../libexosphere/source/pmic/pmic_api.cpp | 135 ++ libraries/libexosphere/source/se/se_aes.cpp | 209 +++ .../libexosphere/source/se/se_execute.cpp | 138 ++ .../libexosphere/source/se/se_execute.hpp | 28 + .../libexosphere/source/se/se_management.cpp | 114 ++ .../libexosphere/source/se/se_registers.hpp | 249 ++++ libraries/libexosphere/source/se/se_rng.cpp | 146 +++ libraries/libexosphere/source/se/se_rsa.cpp | 125 ++ .../libexosphere/source/se/se_suspend.cpp | 59 + .../libexosphere/source/tsec/tsec_api.cpp | 35 + .../libexosphere/source/uart/uart_api.cpp | 113 ++ .../source/uart/uart_registers.hpp | 180 +++ .../libexosphere/source/util/util_api.cpp | 50 + libraries/libexosphere/source/wdt/wdt_api.cpp | 99 ++ .../source/diag/diag_assertion_impl.cpp | 4 + .../libvapours/include/vapours/assert.hpp | 3 +- .../crypto/impl/crypto_ctr_mode_impl.hpp | 2 +- .../libvapours/include/vapours/defines.hpp | 2 + .../libvapours/include/vapours/literals.hpp | 12 +- .../svc/arch/arm/svc_thread_local_region.hpp | 38 + .../svc/svc_select_thread_local_region.hpp | 8 + .../include/vapours/svc/svc_types_common.hpp | 19 +- .../libvapours/include/vapours/types.hpp | 9 +- libraries/libvapours/include/vapours/util.hpp | 1 + .../vapours/util/util_aligned_buffer.hpp | 35 + .../include/vapours/util/util_bitpack.hpp | 2 +- .../include/vapours/util/util_endian.hpp | 8 +- .../include/vapours/util/util_tinymt.hpp | 2 +- 192 files changed, 15093 insertions(+), 24 deletions(-) create mode 100644 exosphere2/Makefile create mode 100644 exosphere2/loader_stub/Makefile create mode 100644 exosphere2/loader_stub/loader_stub.ld create mode 100644 exosphere2/loader_stub/loader_stub.specs create mode 100644 exosphere2/loader_stub/source/secmon_loader_error.cpp create mode 100644 exosphere2/loader_stub/source/secmon_loader_error.hpp create mode 100644 exosphere2/loader_stub/source/secmon_loader_main.cpp create mode 100644 exosphere2/loader_stub/source/secmon_loader_uncompress.cpp create mode 100644 exosphere2/loader_stub/source/secmon_loader_uncompress.hpp create mode 100644 exosphere2/loader_stub/source/start.s create mode 100644 exosphere2/program/Makefile create mode 100644 exosphere2/program/program.ld create mode 100644 exosphere2/program/program.specs create mode 100644 exosphere2/program/sc7fw/Makefile create mode 100644 exosphere2/program/sc7fw/sc7fw.ld create mode 100644 exosphere2/program/sc7fw/sc7fw.specs create mode 100644 exosphere2/program/sc7fw/source/sc7fw_dram.cpp create mode 100644 exosphere2/program/sc7fw/source/sc7fw_dram.hpp create mode 100644 exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s create mode 100644 exosphere2/program/sc7fw/source/sc7fw_main.cpp create mode 100644 exosphere2/program/sc7fw/source/sc7fw_start.s create mode 100644 exosphere2/program/sc7fw/source/sc7fw_util.hpp create mode 100644 exosphere2/program/sc7fw/source/sc7fw_util_asm.s create mode 100644 exosphere2/program/source/boot/secmon_boot.hpp create mode 100644 exosphere2/program/source/boot/secmon_boot_cache.cpp create mode 100644 exosphere2/program/source/boot/secmon_boot_config.cpp create mode 100644 exosphere2/program/source/boot/secmon_boot_functions.cpp create mode 100644 exosphere2/program/source/boot/secmon_boot_functions.hpp create mode 100644 exosphere2/program/source/boot/secmon_boot_setup.cpp create mode 100644 exosphere2/program/source/boot/secmon_crt0.s create mode 100644 exosphere2/program/source/boot/secmon_crt0_cpp.cpp create mode 100644 exosphere2/program/source/boot/secmon_key_data.cpp create mode 100644 exosphere2/program/source/boot/secmon_main.cpp create mode 100644 exosphere2/program/source/boot/secmon_make_page_table.cpp create mode 100644 exosphere2/program/source/boot/secmon_package2.cpp create mode 100644 exosphere2/program/source/boot/secmon_size_data.s create mode 100644 exosphere2/program/source/secmon_cache.inc create mode 100644 exosphere2/program/source/secmon_cache_impl.inc create mode 100644 exosphere2/program/source/secmon_cpu_context.cpp create mode 100644 exosphere2/program/source/secmon_cpu_context.hpp create mode 100644 exosphere2/program/source/secmon_error.cpp create mode 100644 exosphere2/program/source/secmon_error.hpp create mode 100644 exosphere2/program/source/secmon_exception_vectors.s create mode 100644 exosphere2/program/source/secmon_interrupt_handler.cpp create mode 100644 exosphere2/program/source/secmon_interrupt_handler.hpp create mode 100644 exosphere2/program/source/secmon_key_storage.cpp create mode 100644 exosphere2/program/source/secmon_key_storage.hpp create mode 100644 exosphere2/program/source/secmon_misc.cpp create mode 100644 exosphere2/program/source/secmon_misc.hpp create mode 100644 exosphere2/program/source/secmon_setup.cpp create mode 100644 exosphere2/program/source/secmon_setup.hpp create mode 100644 exosphere2/program/source/secmon_setup_warm.cpp create mode 100644 exosphere2/program/source/secmon_start_virtual.s create mode 100644 exosphere2/program/source/smc/secmon_smc_aes.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_aes.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_carveout.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_carveout.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_common.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_error.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_error.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_es.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_es.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_handler.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_handler.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_info.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_info.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_memory_access.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_memory_access.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_power_management.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_power_management.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_random.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_random.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_register_access.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_register_access.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_result.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_result.hpp create mode 100644 exosphere2/program/source/smc/secmon_smc_rsa.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_rsa.hpp create mode 100644 exosphere2/program/split_program.py create mode 100644 libraries/config/arch/arm/arch.mk create mode 100644 libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk create mode 100644 libraries/config/board/nintendo/nx_bpmp/board.mk create mode 100644 libraries/config/templates/exosphere.mk create mode 100644 libraries/libexosphere/Makefile create mode 100644 libraries/libexosphere/arm.mk create mode 100644 libraries/libexosphere/arm64.mk create mode 100644 libraries/libexosphere/include/exosphere.hpp create mode 100644 libraries/libexosphere/include/exosphere/actmon.hpp create mode 100644 libraries/libexosphere/include/exosphere/clkrst.hpp create mode 100644 libraries/libexosphere/include/exosphere/common.hpp create mode 100644 libraries/libexosphere/include/exosphere/fuse.hpp create mode 100644 libraries/libexosphere/include/exosphere/gic.hpp create mode 100644 libraries/libexosphere/include/exosphere/hw.hpp create mode 100644 libraries/libexosphere/include/exosphere/hw/hw_arm.hpp create mode 100644 libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp create mode 100644 libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp create mode 100644 libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp create mode 100644 libraries/libexosphere/include/exosphere/i2c.hpp create mode 100644 libraries/libexosphere/include/exosphere/log.hpp create mode 100644 libraries/libexosphere/include/exosphere/mmu.hpp create mode 100644 libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp create mode 100644 libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp create mode 100644 libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp create mode 100644 libraries/libexosphere/include/exosphere/pmc.hpp create mode 100644 libraries/libexosphere/include/exosphere/pmic.hpp create mode 100644 libraries/libexosphere/include/exosphere/reg.hpp create mode 100644 libraries/libexosphere/include/exosphere/se.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_aes.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_common.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_management.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_rng.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_rsa.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_suspend.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp create mode 100644 libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp create mode 100644 libraries/libexosphere/include/exosphere/tsec.hpp create mode 100644 libraries/libexosphere/include/exosphere/uart.hpp create mode 100644 libraries/libexosphere/include/exosphere/util.hpp create mode 100644 libraries/libexosphere/include/exosphere/wdt.hpp create mode 100644 libraries/libexosphere/source/actmon/actmon_api.cpp create mode 100644 libraries/libexosphere/source/clkrst/clkrst_api.cpp create mode 100644 libraries/libexosphere/source/clkrst/clkrst_registers.hpp create mode 100644 libraries/libexosphere/source/fuse/fuse_api.cpp create mode 100644 libraries/libexosphere/source/fuse/fuse_registers.hpp create mode 100644 libraries/libexosphere/source/gic/gic_api.cpp create mode 100644 libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp create mode 100644 libraries/libexosphere/source/i2c/i2c_api.cpp create mode 100644 libraries/libexosphere/source/i2c/i2c_registers.hpp create mode 100644 libraries/libexosphere/source/libc/libc.c create mode 100644 libraries/libexosphere/source/log/log_api.cpp create mode 100644 libraries/libexosphere/source/pmc/pmc_api.cpp create mode 100644 libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc create mode 100644 libraries/libexosphere/source/pmic/max77620.h create mode 100644 libraries/libexosphere/source/pmic/max7762x.h create mode 100644 libraries/libexosphere/source/pmic/pmic_api.cpp create mode 100644 libraries/libexosphere/source/se/se_aes.cpp create mode 100644 libraries/libexosphere/source/se/se_execute.cpp create mode 100644 libraries/libexosphere/source/se/se_execute.hpp create mode 100644 libraries/libexosphere/source/se/se_management.cpp create mode 100644 libraries/libexosphere/source/se/se_registers.hpp create mode 100644 libraries/libexosphere/source/se/se_rng.cpp create mode 100644 libraries/libexosphere/source/se/se_rsa.cpp create mode 100644 libraries/libexosphere/source/se/se_suspend.cpp create mode 100644 libraries/libexosphere/source/tsec/tsec_api.cpp create mode 100644 libraries/libexosphere/source/uart/uart_api.cpp create mode 100644 libraries/libexosphere/source/uart/uart_registers.hpp create mode 100644 libraries/libexosphere/source/util/util_api.cpp create mode 100644 libraries/libexosphere/source/wdt/wdt_api.cpp create mode 100644 libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp create mode 100644 libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp diff --git a/.gitignore b/.gitignore index 6b4b5309f..54b77b4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ # Executables *.exe +*.lz4 *.out *.app *.i*86 diff --git a/exosphere/src/start.s b/exosphere/src/start.s index 7ac4fe26d..84eb1c2cd 100644 --- a/exosphere/src/start.s +++ b/exosphere/src/start.s @@ -13,7 +13,7 @@ * 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 @@ -109,7 +109,7 @@ __start_cold: stp x3, x4, [x0], #0x10 cmp x0, x2 blo 1b - + adr x19, __start_cold adr x20, g_coldboot_crt0_relocation_list sub x20, x20, x19 @@ -125,7 +125,7 @@ _post_cold_crt0_reloc: bl get_coldboot_crt0_stack_address mov sp, x0 mov fp, #0 - + /* Relocate Exosphere image to free DRAM, clearing the image in IRAM. */ ldr x0, =0x80010000 add x20, x20, x0 @@ -147,7 +147,7 @@ _post_cold_crt0_reloc: ldr x1, =0x80010000 /* Set size in coldboot relocation list. */ str x21, [x0, #0x8] - + bl coldboot_init ldr x16, =__jump_to_main_cold diff --git a/exosphere2/Makefile b/exosphere2/Makefile new file mode 100644 index 000000000..9b72ff1ec --- /dev/null +++ b/exosphere2/Makefile @@ -0,0 +1,38 @@ +TARGETS := exosphere.bin program.lz4 +CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean + +SUBFOLDERS := $(MODULES) + +all: exosphere.bin + +clean: $(CLEAN_TARGETS) + @rm -f exosphere.bin + +exosphere.bin: program.lz4 boot_code.lz4 + $(MAKE) -C loader_stub + @cp loader_stub/loader_stub.bin exosphere.bin + @echo "Built exosphere.bin..." + +program.lz4: check_libexo + $(MAKE) -C program + @cp program/program.lz4 program.lz4 + @cp program/boot_code.lz4 boot_code.lz4 + +boot_code.lz4: program.lz4 + +check_libexo: + @$(MAKE) --no-print-directory -C ../libraries/libexosphere + +exosphere-clean: + $(MAKE) -C loader_stub clean + @rm -f exosphere.bin + +program-clean: + $(MAKE) -C program clean + @rm -f program.lz4 + +boot_code-clean: + $(MAKE) -C boot_code clean + @rm -f boot_code.lz4 + +.PHONY: all clean $(CLEAN_TARGETS) diff --git a/exosphere2/loader_stub/Makefile b/exosphere2/loader_stub/Makefile new file mode 100644 index 000000000..a5725e57e --- /dev/null +++ b/exosphere2/loader_stub/Makefile @@ -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 +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/loader_stub/loader_stub.ld b/exosphere2/loader_stub/loader_stub.ld new file mode 100644 index 000000000..bb8239d81 --- /dev/null +++ b/exosphere2/loader_stub/loader_stub.ld @@ -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) } +} \ No newline at end of file diff --git a/exosphere2/loader_stub/loader_stub.specs b/exosphere2/loader_stub/loader_stub.specs new file mode 100644 index 000000000..03984762f --- /dev/null +++ b/exosphere2/loader_stub/loader_stub.specs @@ -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 diff --git a/exosphere2/loader_stub/source/secmon_loader_error.cpp b/exosphere2/loader_stub/source/secmon_loader_error.cpp new file mode 100644 index 000000000..2f67b507f --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_error.cpp @@ -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(); + } + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_error.hpp b/exosphere2/loader_stub/source/secmon_loader_error.hpp new file mode 100644 index 000000000..611990740 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_error.hpp @@ -0,0 +1,23 @@ +/* + * 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> +#pragma once + +namespace ams::secmon::loader { + + NORETURN void ErrorReboot(); + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere2/loader_stub/source/secmon_loader_main.cpp new file mode 100644 index 000000000..70dc8555a --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_main.cpp @@ -0,0 +1,41 @@ +/* + * 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(); + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp new file mode 100644 index 000000000..a58e3c5a1 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp @@ -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. */ + 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(); + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp new file mode 100644 index 000000000..b820209d5 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp @@ -0,0 +1,23 @@ +/* + * 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 <vapours.hpp> +#pragma once + +namespace ams::secmon::loader { + + void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size); + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/start.s b/exosphere2/loader_stub/source/start.s new file mode 100644 index 000000000..2f432aaa7 --- /dev/null +++ b/exosphere2/loader_stub/source/start.s @@ -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, =0x7C010800 + mov sp, x20 + + /* Call our init array functions. */ + bl __libc_init_array + + /* Uncompress the program and iram boot code images. */ + b _ZN3ams6secmon6loader20UncompressAndExecuteEv diff --git a/exosphere2/program/Makefile b/exosphere2/program/Makefile new file mode 100644 index 000000000..078b88fd9 --- /dev/null +++ b/exosphere2/program/Makefile @@ -0,0 +1,128 @@ +#--------------------------------------------------------------------------------- +# 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)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64 + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).lz4 + +$(OUTPUT).lz4 : $(OUTPUT).bin + @python ../split_program.py $(OUTPUT).bin $(dir $(OUTPUT)) + @echo built ... $(notdir $@) + +$(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 + +secmon_crt0_cpp.o secmon_make_page_table.o : CFLAGS += -fno-builtin + +%.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 +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/program.ld b/exosphere2/program/program.ld new file mode 100644 index 000000000..61e8720dc --- /dev/null +++ b/exosphere2/program/program.ld @@ -0,0 +1,277 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + unused_region : ORIGIN = 0x1000, LENGTH = 4K + iram_boot_code : ORIGIN = 0x040032000, LENGTH = 48K + tzram : ORIGIN = 0x07C010000, LENGTH = 64K + + /* Warmboot code follows the vectors in memory. */ + /* However, we can't know for sure how big warmboot is, so we'll just say it's 2K. */ + warmboot_text : ORIGIN = ORIGIN(tzram) + 10K, LENGTH = 2K + + main : ORIGIN = 0x1F00C0000, LENGTH = 48K + tzram_boot_code : ORIGIN = 0x1F01C0800, LENGTH = 6K + + glob : ORIGIN = 0x040032000, LENGTH = 64K +} + +SECTIONS +{ + .metadata : + { + . = ALIGN(8); + KEEP (*(.metadata .metadata.*)) + . = ALIGN(8); + } >unused_region AT>glob + + PROVIDE(__glob_start__ = ORIGIN(glob)); + . = __glob_start__; + + __bootcode_start__ = ABSOLUTE(.); + + .crt0 : + { + KEEP (*(.crt0 .crt0.*)) + KEEP (secmon_crt0_cpp.o(.text*)) + KEEP (secmon_boot_cache.o(.text*)) + KEEP (secmon_make_page_table.o(.text*)) + *(.crt0.rodata*) + secmon_crt0_cpp.o(.rodata*) + secmon_boot_cache.o(.rodata*) + secmon_make_page_table.o(.rodata*) + *(.crt0.data*) + secmon_crt0_cpp.o(.data*) + secmon_boot_cache.o(.data*) + secmon_make_page_table.o(.data*) + . = ALIGN(8); + } >iram_boot_code AT>glob + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >iram_boot_code AT>glob + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >iram_boot_code AT>glob + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >iram_boot_code 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)) + } >iram_boot_code AT>glob + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >iram_boot_code AT>glob + + __bootcode_end__ = ABSOLUTE(.); + + __program_start__ = ABSOLUTE(.); + .tzram_boot_code : + { + KEEP(secmon_main.o(.text*)) + KEEP(secmon_boot_functions.o(.text*)) + KEEP(secmon_boot_config.o(.text*)) + KEEP(secmon_boot_setup.o(.text*)) + KEEP(secmon_package2.o(.text*)) + KEEP(secmon_key_data.o(.text*)) + secmon_main.o(.rodata*) + secmon_boot_functions.o(.rodata*) + secmon_boot_config.o(.rodata*) + secmon_boot_setup.o(.rodata*) + secmon_package2.o(.rodata*) + secmon_key_data.o(.rodata*) + secmon_main.o(.data*) + secmon_boot_functions.o(.data*) + secmon_boot_config.o(.data*) + secmon_boot_setup.o(.data*) + secmon_package2.o(.data*) + secmon_key_data.o(.data*) + . = ALIGN(8); + } >tzram_boot_code AT>glob + + .tzram_boot_code.bss : + { + __boot_bss_start__ = ABSOLUTE(.); + secmon_main.o(.bss* COMMON) + secmon_boot_functions.o(.bss* COMMON) + secmon_boot_config.o(.bss* COMMON) + secmon_boot_setup.o(.bss* COMMON) + secmon_package2.o(.bss* COMMON) + secmon_key_data.o(.bss* COMMON) + __boot_bss_end__ = ABSOLUTE(.); + } >tzram_boot_code AT>glob + + .tzram_boot_code.fill : + { + FILL(0x00000000); + . = ORIGIN(tzram_boot_code) + LENGTH(tzram_boot_code) - 1; + BYTE(0x00); + } > tzram_boot_code AT>glob + + .vectors : + { + KEEP (*(.vectors*)) + . = ALIGN(0x100); + } >main AT>glob + + + .warmboot : + { + KEEP (*(.warmboot.text.start)) /* Should be first */ + KEEP (*(.warmboot.text*)) + KEEP(secmon_setup_warm.o(.text*)) + KEEP(tsec_*.o(.text*)) + KEEP (*(.warmboot.rodata*)) + KEEP(secmon_setup_warm.o(.rodata*)) + KEEP(tsec_*.o(.rodata*)) + KEEP (*(.warmboot.data*)) + KEEP(secmon_setup_warm.o(.data*)) + KEEP(tsec_*.o(.data*)) + } >warmboot_text AT>glob + + .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); + } >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 section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >main AT>glob + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >main AT>glob + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >main AT>glob + + .hash : { *(.hash) } >main AT>glob + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >main AT>glob + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >main AT>glob + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >main AT>glob + .got.plt : { *(.got.plt) *(.igot.plt) } >main AT>glob + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >main AT>glob + + .bss ALIGN(8) (NOLOAD) : + { + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + __bss_end__ = ABSOLUTE(.); + } >main AT>glob + + __program_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) } +} \ No newline at end of file diff --git a/exosphere2/program/program.specs b/exosphere2/program/program.specs new file mode 100644 index 000000000..22b789960 --- /dev/null +++ b/exosphere2/program/program.specs @@ -0,0 +1,4 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles \ No newline at end of file diff --git a/exosphere2/program/sc7fw/Makefile b/exosphere2/program/sc7fw/Makefile new file mode 100644 index 000000000..31439e834 --- /dev/null +++ b/exosphere2/program/sc7fw/Makefile @@ -0,0 +1,122 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# 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)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +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) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere2/program/sc7fw/sc7fw.ld new file mode 100644 index 000000000..e8a097dbb --- /dev/null +++ b/exosphere2/program/sc7fw/sc7fw.ld @@ -0,0 +1,183 @@ +OUTPUT_ARCH(arm) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + sc7fw : ORIGIN = 0x40003000, LENGTH = 4K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(sc7fw)); + . = __start__; + __code_start = . ; + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >sc7fw + + .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); + } >sc7fw + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >sc7fw + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >sc7fw + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >sc7fw + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >sc7fw + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >sc7fw + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >sc7fw + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >sc7fw + + .hash : { *(.hash) } >sc7fw + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >sc7fw + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >sc7fw + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >sc7fw + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >sc7fw + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >sc7fw + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >sc7fw + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >sc7fw + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >sc7fw + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >sc7fw + .got.plt : { *(.got.plt) *(.igot.plt) } >sc7fw + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >sc7fw + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >sc7fw + __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) } +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/sc7fw.specs b/exosphere2/program/sc7fw/sc7fw.specs new file mode 100644 index 000000000..13455dc90 --- /dev/null +++ b/exosphere2/program/sc7fw/sc7fw.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp new file mode 100644 index 000000000..dcdf9c94b --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp @@ -0,0 +1,182 @@ +/* + * 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 "sc7fw_util.hpp" +#include "sc7fw_dram.hpp" + +namespace ams::sc7fw { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + void UpdateEmcTiming() { + /* Enable timing update. */ + reg::Write(EMC_ADDRESS(EMC_TIMING_CONTROL), EMC_REG_BITS_ENUM(TIMING_CONTROL_TIMING_UPDATE, ENABLED)); + + /* Wait for the timing update to complete. */ + while (!reg::HasValue(EMC_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, DONE))) { + /* ... */ + } + } + + void RequestAllPadsPowerDown(uintptr_t addr, uintptr_t expected) { + constexpr u32 DpdAllRequestValue = reg::Encode(PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_ON)) | 0x0FFFFFFF; + const auto RequestAddress = addr; + const auto StatusAddress = addr + 4; + + /* Request all pads enter power down. */ + reg::Write(PMC + RequestAddress, DpdAllRequestValue); + + /* Wait until the status reflects our expectation (and all pads are shut down). */ + while (reg::Read(PMC + StatusAddress) != expected) { /* ... */ } + + /* Wait a little while to allow the power down status to propagate. */ + SpinLoop(0x20); + }; + + } + + void SaveEmcFsp() { + /* We require that the RAM is LPDDR4. */ + AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG5), EMC_REG_BITS_ENUM(FBIO_CFG5_DRAM_TYPE, LPDDR4))); + + /* Read the frequency set points from MRW3. */ + constexpr u32 FspShift = 6; + constexpr u32 FspBits = 2; + constexpr u32 FspMask = ((1u << FspBits) - 1) << FspShift; + static_assert(FspMask == 0x000000C0); + const u32 fsp = (reg::Read(EMC_ADDRESS(EMC_MRW3)) & FspMask) >> FspShift; + + /* Write the fsp to PMC_SCRATCH18, where it will be restored to MRW3 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH18, REG_BITS_VALUE(FspShift, FspBits, fsp)); + + /* Write the fsp twice to PMC_SCRATCH12, where it will be restored to MRW12 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH12, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp)); + + /* Write the fsp twice to PMC_SCRATCH13, where it will be restored to MRW13 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH13, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp)); + } + + void EnableSdramSelfRefresh() { + /* We require that the RAM is dual-channel. */ + AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG7), EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))); + + /* Disable RAM's ability to dynamically self-refresh, and to opportunistically perform powerdown. */ + reg::Write(EMC_ADDRESS(EMC_CFG), EMC_REG_BITS_ENUM(CFG_DYN_SELF_REF, DISABLED), + EMC_REG_BITS_ENUM(CFG_DRAM_ACPD, NO_POWERDOWN)); + + /* Update the EMC timing. */ + UpdateEmcTiming(); + + /* Wait five microseconds. */ + util::WaitMicroSeconds(5); + + /* Disable ZQ calibration. */ + reg::Write(EMC_ADDRESS(EMC_ZCAL_INTERVAL), 0); + + /* Disable automatic calibration. */ + reg::Write(EMC_ADDRESS(EMC_AUTO_CAL_CONFIG), EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, ENABLE), + EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, ENABLE), + EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, DISABLE)); + + /* Get whether digital delay locked loops are enabled. */ + const bool has_dll = reg::HasValue(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, ENABLED)); + if (has_dll) { + /* If they are, disable them. */ + reg::ReadWrite(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED)); + } + + /* Update the EMC timing. */ + UpdateEmcTiming(); + + /* If dll was enabled, wait until both EMC0 and EMC1 have dll disabled. */ + if (has_dll) { + while (!reg::HasValue(EMC0_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ } + } + + /* Stall all reads and writes. */ + reg::Write(EMC_ADDRESS(EMC_REQ_CTRL), EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_READS, 1), + EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_WRITES, 1)); + + /* Wait until both EMC0 and EMC1 have no outstanding transactions. */ + while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ } + + /* Enable self-refresh. */ + reg::Write(EMC_ADDRESS(EMC_SELF_REF), EMC_REG_BITS_ENUM(SELF_REF_SREF_DEV_SELECTN, BOTH), + EMC_REG_BITS_ENUM(SELF_REF_SELF_REF_CMD, ENABLED)); + + /* Wait until both EMC and EMC1 are in self-refresh. */ + const auto desired = reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2)) ? EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, BOTH_ENABLED) + : EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, ENABLED); + + /* NOTE: Nintendo's sc7 entry firmware has a bug here. */ + /* Instead of waiting for both EMCs to report self-refresh, they just read the EMC_STATUS for each EMC. */ + /* This is incorrect, per documentation. */ + while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ } + } + + void EnableEmcAllSegmentsRefresh() { + constexpr int MR17_PASR_Segment = 17; + + /* Write zeros to MR17_PASR_Segment to enable refresh for all segments for dev0. */ + reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV0), + EMC_REG_BITS_ENUM (MRW_CNT, EXT1), + EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment), + EMC_REG_BITS_VALUE(MRW_OP, 0)); + + /* If dev1 exists, do the same for dev1. */ + if (reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2))) { + reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV1), + EMC_REG_BITS_ENUM (MRW_CNT, EXT1), + EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment), + EMC_REG_BITS_VALUE(MRW_OP, 0)); + } + } + + void EnableDdrDeepPowerDown() { + /* Read and decode the parameters Nintendo stores in EMC_PMC_SCRATCH3. */ + const u32 scratch3 = reg::Read(EMC_ADDRESS(EMC_PMC_SCRATCH3)); + const bool weak_bias = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_WEAK_BIAS))) == reg::EncodeValue(EMC_REG_BITS_ENUM(PMC_SCRATCH3_WEAK_BIAS, ENABLED)); + const u32 ddr_cntrl = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_DDR_CNTRL))); + + /* Write the decoded value to PMC_DDR_CNTRL. */ + reg::Write(PMC + APBDEV_PMC_DDR_CNTRL, ddr_cntrl); + + /* If weak bias is enabled, set all VTT_E_WB bits in APBDEV_PMC_WEAK_BIAS. */ + if (weak_bias) { + constexpr u32 WeakBiasVttEWbAll = 0x7FFF0000; + reg::Write(PMC + APBDEV_PMC_WEAK_BIAS, WeakBiasVttEWbAll); + } + + /* Request that DPD3 pads power down. */ + constexpr u32 EristaDpd3Mask = 0x0FFFFFFF; + constexpr u32 MarikoDpd3Mask = 0x0FFF9FFF; + if (true /* TODO: IsErista */) { + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, EristaDpd3Mask); + } else { + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, MarikoDpd3Mask); + } + + /* Request that DPD4 pads power down. */ + constexpr u32 Dpd4Mask = 0x0FFF1FFF; + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD4_REQ, Dpd4Mask); + } + +} diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.hpp b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp new file mode 100644 index 000000000..dca0a47b0 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::sc7fw { + + void SaveEmcFsp(); + void EnableSdramSelfRefresh(); + void EnableEmcAllSegmentsRefresh(); + void EnableDdrDeepPowerDown(); + +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s new file mode 100644 index 000000000..ca8c3b787 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s @@ -0,0 +1,22 @@ +/* + * 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/>. + */ + +.section .vectors, "ax", %progbits +.align 3 +.global reset +reset: + b _start + b _ZN3ams5sc7fw16ExceptionHandlerEv diff --git a/exosphere2/program/sc7fw/source/sc7fw_main.cpp b/exosphere2/program/sc7fw/source/sc7fw_main.cpp new file mode 100644 index 000000000..0a082586a --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_main.cpp @@ -0,0 +1,116 @@ +/* + * 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 "sc7fw_util.hpp" +#include "sc7fw_dram.hpp" + +namespace ams::sc7fw { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + void DisableCrail() { + /* Wait for CRAIL to be off. */ + while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, OFF))) { /* ... */ } + + /* Set CRAIL to be clamped. */ + reg::ReadWrite(PMC + APBDEV_PMC_SET_SW_CLAMP, PMC_REG_BITS_VALUE(SET_SW_CLAMP_CRAIL, 1)); + + /* Wait for CRAIL to be clamped. */ + while (!reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } + + /* Spin loop for a short while, to allow time for the clamp to take effect. */ + sc7fw::SpinLoop(10); + + /* Initialize i2c-5. */ + i2c::Initialize(i2c::Port_5); + + /* Disable the voltage to CPU. */ + pmic::DisableVddCpu(fuse::GetRegulator()); + + /* Wait 700 microseconds to ensure voltage is disabled. */ + util::WaitMicroSeconds(700); + } + + void DisableAllInterrupts() { + /* Disable all interrupts for bpmp in all interrupt controllers. */ + reg::Write(PRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(SEC_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(TRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(QUAD_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(PENTA_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(HEXA_ICTLR(ICTLR_COP_IER_CLR), ~0u); + } + + void EnterSc7() { + /* Disable read buffering and write buffering in the BPMP cache. */ + reg::ReadWrite(AVP_CACHE_ADDRESS(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(DISABLE_WB, TRUE), + AVP_CACHE_REG_BITS_ENUM(DISABLE_RB, TRUE)); + + /* Ensure the CPU Rail is turned off. */ + DisableCrail(); + + /* Disable all interrupts. */ + DisableAllInterrupts(); + + /* Save the EMC FSP */ + SaveEmcFsp(); + + /* Enable self-refresh for DRAM */ + EnableSdramSelfRefresh(); + + /* Enable refresh for all EMC devices. */ + EnableEmcAllSegmentsRefresh(); + + /* Enable deep power-down for ddr. */ + EnableDdrDeepPowerDown(); + + /* Enable pad sampling during deep sleep. */ + reg::ReadWrite(PMC + APBDEV_PMC_DPD_SAMPLE, PMC_REG_BITS_ENUM(DPD_SAMPLE_ON, ENABLE)); + reg::Read(PMC + APBDEV_PMC_DPD_SAMPLE); + + /* Wait a while for pad sampling to be enabled. */ + sc7fw::SpinLoop(0x128); + + /* Enter deep sleep. */ + reg::ReadWrite(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_ON, ENABLE)); + + /* Wait forever until we're asleep. */ + while (true) { /* ... */ } + } + + } + + void Main() { + EnterSc7(); + } + + NORETURN void ExceptionHandler() { + /* Write enable to MAIN_RESET. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); + while (true) { /* ... */ } + } + +} + +namespace ams::diag { + + void AbortImpl() { + sc7fw::ExceptionHandler(); + } + +} diff --git a/exosphere2/program/sc7fw/source/sc7fw_start.s b/exosphere2/program/sc7fw/source/sc7fw_start.s new file mode 100644 index 000000000..02761c112 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_start.s @@ -0,0 +1,35 @@ +/* + * 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/>. + */ + +.section .text.start, "ax", %progbits +.align 3 +.global _start +_start: + /* Set CPSR_cf and CPSR_cf. */ + msr cpsr_f, #0xC0 + msr cpsr_cf, #0xD3 + + /* Set the stack pointer. */ + ldr sp, =0x40005000 + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams5sc7fw16ExceptionHandlerEv + + /* Invoke main. */ + bl _ZN3ams5sc7fw4MainEv + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util.hpp b/exosphere2/program/sc7fw/source/sc7fw_util.hpp new file mode 100644 index 000000000..2202deb0f --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_util.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::sc7fw { + + void SpinLoop(unsigned int num); + +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s new file mode 100644 index 000000000..249c5b0f9 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s @@ -0,0 +1,30 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams5sc7fw8SpinLoopEj, "ax", %progbits +.global _ZN3ams5sc7fw8SpinLoopEj +.thumb_func +.syntax unified +_ZN3ams5sc7fw8SpinLoopEj: + 1: + /* Subtract one from the count. */ + subs r0, r0, #1 + + /* If we aren't at zero, continue looping. */ + bgt 1b + + /* Return. */ + bx lr \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp new file mode 100644 index 000000000..dca6d0091 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon::boot { + + void MakePageTable(); + + void InitializeColdBoot(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere2/program/source/boot/secmon_boot_cache.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_cache.cpp @@ -0,0 +1,22 @@ +/* + * 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> + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_config.cpp @@ -0,0 +1,22 @@ +/* + * 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> + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp new file mode 100644 index 000000000..3095f2f77 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -0,0 +1,26 @@ +/* + * 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_boot_functions.hpp" + +namespace ams::secmon::boot { + + void ClearIram() { + /* Clear the boot code image from where it was loaded in IRAM. */ + util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize()); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp new file mode 100644 index 000000000..5ec201add --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon::boot { + + void ClearIram(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp new file mode 100644 index 000000000..06b411f87 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -0,0 +1,337 @@ +/* + * 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_boot.hpp" +#include "../secmon_setup.hpp" +#include "../secmon_key_storage.hpp" + +namespace ams::secmon::boot { + + namespace { + + void ValidateSystemCounters() { + const uintptr_t sysctr0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); + + /* Validate the system counter frequency is as expected. */ + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_CNTFID0) == 19'200'000u); + + /* Validate the system counters are as expected. */ + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 0)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 1)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 2)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 3)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 4)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 5)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 6)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 7)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 8)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 9)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(10)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(11)) == 0); + } + + void SetupPmcRegisters() { + const auto pmc = MemoryRegionVirtualDevicePmc.GetAddress(); + + /* Set the physical address of the warmboot binary to scratch 1. */ + reg::Write(pmc + APBDEV_PMC_SCRATCH1, static_cast<u32>(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress())); + + /* Configure logging by setting bits 18-19 of scratch 20. */ + reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH20, REG_BITS_VALUE(18, 2, 0)); + + /* Clear the wdt reset flag. */ + reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); + + /* Configure warmboot to set Set FUSE_PRIVATEKEYDISABLE to KEY_INVISIBLE. */ + reg::ReadWrite(pmc + APBDEV_PMC_SECURE_SCRATCH21, REG_BITS_VALUE(4, 1, 1)); + + /* Write the warmboot key. */ + /* TODO */ + } + + /* This function derives the master kek and device keys using the tsec root key. */ + /* NOTE: Exosphere does not use this in practice, and expects the bootloader to set up keys already. */ + /* NOTE: This function is currently not implemented. If implemented, it will only be a reference implementation. */ + [[maybe_unused]] + void DeriveMasterKekAndDeviceKey() { + /* TODO: Decide whether to implement this. */ + } + + void SetupRandomKey(int slot, se::KeySlotLockFlags flags) { + /* Create an aligned buffer to hold the key. */ + constexpr size_t KeySize = se::AesBlockSize; + util::AlignedBuffer<hw::DataCacheLineSize, KeySize> key; + + /* Ensure data is consistent before triggering the SE. */ + hw::FlushDataCache(key, KeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes into the key. */ + se::GenerateRandomBytes(key, KeySize); + + /* Ensure that the CPU sees consistent data. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(key, KeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Use the random bytes as a key source. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_DeviceMaster, key, KeySize); + + /* Lock the keyslot. */ + se::LockAesKeySlot(slot, flags); + } + + constinit const u8 MasterKeyVectorsDev[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = { + {0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE}, /* Zeroes encrypted with Master Key 00. */ + {0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23}, /* Master key 00 encrypted with Master key 01. */ + {0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D}, /* Master key 01 encrypted with Master key 02. */ + {0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */ + {0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */ + {0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */ + {0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */ + {0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */ + {0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */ + {0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */ + {0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */ + }; + + constinit const u8 MasterKeyVectorsProd[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = { + {0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */ + {0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */ + {0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */ + {0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */ + {0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */ + {0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */ + {0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */ + {0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */ + {0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */ + {0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */ + {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */ + }; + + bool TestKeyGeneration(int generation, bool is_prod) { + /* Decrypt the vector chain from generation to start. */ + int slot = pkg1::AesKeySlot_Master; + for (int i = generation; i > 0; --i) { + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize); + slot = pkg1::AesKeySlot_Temporary; + } + + /* Decrypt the final vector. */ + u8 test_vector[se::AesBlockSize]; + se::DecryptAes128(test_vector, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[0] : MasterKeyVectorsDev[0], se::AesBlockSize); + + constexpr u8 ZeroBlock[se::AesBlockSize] = {}; + return crypto::IsSameBytes(ZeroBlock, test_vector, se::AesBlockSize); + } + + int DetermineKeyGeneration(bool is_prod) { + /* Test each generation in order. */ + for (int generation = 0; generation < pkg1::KeyGeneration_Count; ++generation) { + if (TestKeyGeneration(generation, is_prod)) { + return generation; + } + } + + /* We must have found a correct key generation. */ + AMS_ABORT(); + } + + void DeriveAllMasterKeys(bool is_prod, u8 * const work_block) { + + /* Determine the generation. */ + const int generation = DetermineKeyGeneration(is_prod); + + /* Set the global generation. */ + ::ams::secmon::impl::SetKeyGeneration(generation); + + /* Derive all old keys. */ + int slot = pkg1::AesKeySlot_Master; + for (int i = generation; i > 0; --i) { + /* Decrypt the old master key. */ + se::DecryptAes128(work_block, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize); + + /* Set the old master key. */ + SetMasterKey(generation - 1, work_block, se::AesBlockSize); + + /* Set the old master key into a temporary keyslot. */ + se::SetAesKey(pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize); + + /* Perform the next decryption with the older master key. */ + slot = pkg1::AesKeySlot_Temporary; + } + } + + constinit const u8 DeviceMasterKeySourceSources[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = { + {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.0.0 Device Master Key Source Source. */ + {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.0.0 Device Master Key Source Source. */ + {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.0.0 Device Master Key Source Source. */ + {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 Device Master Key Source Source. */ + {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 Device Master Key Source Source. */ + {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 Device Master Key Source Source. */ + {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 Device Master Key Source Source. */ + {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 Device Master Key Source Source. */ + }; + + constinit const u8 DeviceMasterKekSourcesDev[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = { + {0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34}, /* 4.0.0 Device Master Kek Source. */ + {0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8}, /* 5.0.0 Device Master Kek Source. */ + {0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.0.0 Device Master Kek Source. */ + {0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 Device Master Kek Source. */ + {0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 Device Master Kek Source. */ + {0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 Device Master Kek Source. */ + {0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 Device Master Kek Source. */ + {0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B}, /* 9.1.0 Device Master Kek Source. */ + }; + + constinit const u8 DeviceMasterKekSourcesProd[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = { + {0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.0.0 Device Master Kek Source. */ + {0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.0.0 Device Master Kek Source. */ + {0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.0.0 Device Master Kek Source. */ + {0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 Device Master Kek Source. */ + {0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 Device Master Kek Source. */ + {0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 Device Master Kek Source. */ + {0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 Device Master Kek Source. */ + {0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 Device Master Kek Source. */ + }; + + void DeriveAllDeviceMasterKeys(bool is_prod, u8 * const work_block) { + /* Get the current key generation. */ + const int current_generation = secmon::GetKeyGeneration(); + + /* Iterate for all generations. */ + for (int i = 0; i < pkg1::OldDeviceMasterKeyCount; ++i) { + const int generation = pkg1::KeyGeneration_4_0_0 + i; + + /* Load the first master key into the temporary keyslot keyslot. */ + LoadMasterKey(pkg1::AesKeySlot_Temporary, pkg1::KeyGeneration_1_0_0); + + /* Decrypt the device master kek for the generation. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, pkg1::AesKeySlot_Temporary, is_prod ? DeviceMasterKekSourcesProd[i] : DeviceMasterKekSourcesDev[i], se::AesBlockSize); + + /* Decrypt the device master key source into the work block. */ + se::DecryptAes128(work_block, se::AesBlockSize, pkg1::AesKeySlot_DeviceMasterKeySourceKek, DeviceMasterKeySourceSources[i], se::AesBlockSize); + + /* If we're decrypting the current device master key, decrypt into the keyslot. */ + if (generation == current_generation) { + se::SetEncryptedAesKey128(pkg1::AesKeySlot_DeviceMaster, pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize); + } else { + /* Otherwise, decrypt the work block into itself and set the old device master key. */ + se::DecryptAes128(work_block, se::AesBlockSize, pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize); + + /* Set the device master key. */ + SetDeviceMasterKey(generation, work_block, se::AesBlockSize); + } + } + + /* Clear and lock the Device Master Key Source Kek. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_DeviceMasterKeySourceKek); + se::LockAesKeySlot(pkg1::AesKeySlot_DeviceMasterKeySourceKek, se::KeySlotLockFlags_AllLockKek); + } + + void DeriveAllKeys() { + /* Determine whether we're prod. */ + const bool is_prod = fuse::GetHardwareState() != fuse::HardwareState_Development; + + /* Get the ephemeral work block. */ + u8 * const work_block = se::GetEphemeralWorkBlock(); + ON_SCOPE_EXIT { util::ClearMemory(work_block, se::AesBlockSize); }; + + /* Lock the master key as a key. */ + se::LockAesKeySlot(pkg1::AesKeySlot_Master, se::KeySlotLockFlags_AllLockKey); + + /* Setup a random key to protect the old master and device master keys. */ + SetupRandomKey(pkg1::AesKeySlot_RandomForKeyStorageWrap, se::KeySlotLockFlags_AllLockKey); + + /* Derive the master keys. */ + DeriveAllMasterKeys(is_prod, work_block); + + /* Derive the device master keys. */ + DeriveAllDeviceMasterKeys(is_prod, work_block); + + /* Lock the device master key as a kek. */ + se::LockAesKeySlot(pkg1::AesKeySlot_DeviceMaster, se::KeySlotLockFlags_AllLockKek); + + /* Setup a random key to protect user keys. */ + SetupRandomKey(pkg1::AesKeySlot_RandomForUserWrap, se::KeySlotLockFlags_AllLockKek); + } + + void InitializeKeys() { + /* Read lock all aes keys. */ + for (int i = 0; i < se::AesKeySlotCount; ++i) { + se::LockAesKeySlot(i, se::KeySlotLockFlags_AllReadLock); + } + + /* Lock the secure monitor aes keys to be secmon only and non-readable. */ + for (int i = pkg1::AesKeySlot_SecmonStart; i < pkg1::AesKeySlot_SecmonEnd; ++i) { + se::LockAesKeySlot(i, se::KeySlotLockFlags_KeyUse | se::KeySlotLockFlags_PerKey); + } + + /* Lock the unused keyslots entirely. */ + static_assert(pkg1::AesKeySlot_UserEnd <= pkg1::AesKeySlot_SecmonStart); + for (int i = pkg1::AesKeySlot_UserEnd; i < pkg1::AesKeySlot_SecmonStart; ++i) { + se::LockAesKeySlot(i, se::KeySlotLockFlags_AllLockKek); + } + + /* Read lock all rsa keys. */ + for (int i = 0; i < se::RsaKeySlotCount; ++i) { + se::LockRsaKeySlot(i, se::KeySlotLockFlags_KeyUse | se::KeySlotLockFlags_PerKey | se::KeySlotLockFlags_KeyRead); + } + + /* Initialize the rng. */ + se::InitializeRandom(); + + /* Derive the master kek and device key. */ + if constexpr (false) { + DeriveMasterKekAndDeviceKey(); + } + + /* Lock the device key as only usable as a kek. */ + se::LockAesKeySlot(pkg1::AesKeySlot_Device, se::KeySlotLockFlags_AllLockKek); + + /* Derive all keys. */ + DeriveAllKeys(); + } + + } + + void InitializeColdBoot() { + /* Ensure that the system counters are valid. */ + ValidateSystemCounters(); + + /* Set the security engine to Tzram Secure. */ + se::SetTzramSecure(); + + /* Set the security engine to Per Key Secure. */ + se::SetPerKeySecure(); + + /* Setup the PMC registers. */ + SetupPmcRegisters(); + + /* Lockout the scratch that we've just written. */ + /* pmc::LockSecureRegisters(1); */ + + /* Generate a random srk. */ + se::GenerateSrk(); + + /* Initialize the SE keyslots. */ + InitializeKeys(); + + /* Save a test vector for the SE keyslots. */ + SaveSecurityEngineAesKeySlotTestVector(); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_crt0.s b/exosphere2/program/source/boot/secmon_crt0.s new file mode 100644 index 000000000..44d24ebcc --- /dev/null +++ b/exosphere2/program/source/boot/secmon_crt0.s @@ -0,0 +1,37 @@ +/* + * 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/>. + */ + +.section .crt0.text.start, "ax", %progbits +.align 6 +.global _start +_start: + /* mask all interrupts */ + msr daifset, #0xF + + /* Set the stack pointer to a temporary location. */ + ldr x20, =0x7C010800 + mov sp, x20 + + /* Initialize all memory to expected state. */ + ldr x0, =__bss_start__ + ldr x1, =__bss_end__ + ldr x2, =__boot_bss_start__ + ldr x3, =__boot_bss_end__ + bl _ZN3ams6secmon4boot10InitializeEmmmm + + /* Jump to the first bit of virtual code. */ + ldr x16, =_ZN3ams6secmon5StartEv + br x16 diff --git a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp new file mode 100644 index 000000000..35b993ee5 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp @@ -0,0 +1,46 @@ +/* + * 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_boot.hpp" +#include "../secmon_setup.hpp" + +extern "C" void __libc_init_array(); + +namespace ams::secmon::boot { + + void Initialize(uintptr_t bss_start, size_t bss_end, uintptr_t boot_bss_start, uintptr_t boot_bss_end) { + /* Set our start time. */ + auto &secmon_params = *MemoryRegionPhysicalDeviceBootloaderParams.GetPointer<pkg1::SecureMonitorParameters>(); + secmon_params.secmon_start_time = *reinterpret_cast<volatile u32 *>(MemoryRegionPhysicalDeviceTimer.GetAddress() + 0x10); + + /* Setup DMA controllers. */ + SetupSocDmaControllers(); + + /* Make the page table. */ + MakePageTable(); + + /* Setup memory controllers the MMU. */ + SetupCpuMemoryControllersEnableMmu(); + + /* Clear bss. */ + std::memset(reinterpret_cast<void *>(bss_start), 0, bss_end - bss_start); + std::memset(reinterpret_cast<void *>(boot_bss_start), 0, boot_bss_end - boot_bss_start); + + /* Call init array. */ + __libc_init_array(); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_key_data.cpp b/exosphere2/program/source/boot/secmon_key_data.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_key_data.cpp @@ -0,0 +1,22 @@ +/* + * 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> + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp new file mode 100644 index 000000000..738452dab --- /dev/null +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -0,0 +1,69 @@ +/* + * 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_boot.hpp" +#include "secmon_boot_functions.hpp" +#include "../secmon_setup.hpp" +#include "../secmon_misc.hpp" + +namespace ams::secmon { + + void Main() { + /* Set library register addresses. */ + /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ + clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); + /* flowctrl::SetRegisterAddress(); */ + fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); + gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); + i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); + i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress()); + /* pinmux::SetRegisterAddress(); */ + pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); + se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); + uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); + wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + + /* Get the secure monitor parameters. */ + auto &secmon_params = *reinterpret_cast<pkg1::SecureMonitorParameters *>(MemoryRegionVirtualDeviceBootloaderParams.GetAddress()); + + /* Perform initialization. */ + { + /* Perform initial setup. */ + /* This checks the security engine's validity, and configures common interrupts in the GIC. */ + /* This also initializes the global configuration context. */ + secmon::Setup1(); + + /* Save the boot info. */ + secmon::SaveBootInfo(secmon_params); + + /* Perform cold-boot specific init. */ + secmon::boot::InitializeColdBoot(); + + /* Setup the SoC security measures. */ + secmon::SetupSocSecurity(); + + /* TODO: More init. */ + + /* Clear the crt0 code that was present in iram. */ + secmon::boot::ClearIram(); + + /* Alert the bootloader that we're initialized. */ + secmon_params.secmon_state = pkg1::SecureMonitorState_Initialized; + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp new file mode 100644 index 000000000..dfde30d16 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp @@ -0,0 +1,147 @@ +/* + * 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_setup.hpp" +#include "secmon_boot.hpp" + +namespace ams::secmon::boot { + + namespace { + + using namespace ams::mmu; + + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoCode, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwCode, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); + + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexDevice); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice); + + + constexpr void ClearMemory(volatile u64 *start, size_t size) { + volatile u64 * const end = start + (size / sizeof(u64)); + + for (volatile u64 *cur = start; cur < end; ++cur) { + *cur = 0; + } + } + + constexpr void MakePageTablesImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Setup the L1 table. */ + { + ClearMemory(l1, MemoryRegionPhysicalTzramL1PageTable.GetSize()); + + /* Create an L1 table entry for the physical region. */ + SetL1TableEntry(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + static_assert(GetL1EntryIndex(MemoryRegionPhysical.GetAddress()) == 1); + + /* Create an L1 mapping entry for dram. */ + SetL1BlockEntry(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize(), MappingAttributesEl3NonSecureRwData); + + /* Create an L1 table entry for the virtual region. */ + SetL1TableEntry(l1, MemoryRegionVirtual.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + } + + /* Setup the L2 table. */ + { + ClearMemory(l2, MemoryRegionPhysicalTzramL2L3PageTable.GetSize()); + + /* Create an L2 table entry for the virtual region. */ + SetL2TableEntry(l2, MemoryRegionVirtualL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + + /* Create an L2 table entry for the physical iram region. */ + SetL2TableEntry(l2, MemoryRegionPhysicalIramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + + /* Create an L2 table entry for the physical tzram region. */ + SetL2TableEntry(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + } + + /* Setup the L3 table. */ + { + /* L2 and L3 share a page table. */ + if (l2 != l3) { + ClearMemory(l3, MemoryRegionPhysicalTzramL2L3PageTable.GetSize()); + } + + /* Identity-map TZRAM as rwx. */ + SetL3BlockEntry(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Identity-map IRAM boot code as rwx. */ + SetL3BlockEntry(l3, MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Map all devices. */ + { + #define MAP_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \ + SetL3BlockEntry(l3, MemoryRegionVirtualDevice##_NAME_.GetAddress(), MemoryRegionPhysicalDevice##_NAME_.GetAddress(), MemoryRegionVirtualDevice##_NAME_.GetSize(), _SECURE_ ? MappingAttributesEl3SecureDevice : MappingAttributesEl3NonSecureDevice); + + AMS_SECMON_FOREACH_DEVICE_REGION(MAP_DEVICE_REGION); + + #undef MAP_DEVICE_REGION + } + + /* Map the IRAM SC7 work region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Work.GetAddress(), MemoryRegionPhysicalIramSc7Work.GetAddress(), MemoryRegionVirtualIramSc7Work.GetSize(), MappingAttributesEl3NonSecureDevice); + + /* Map the IRAM SC7 firmware region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Firmware.GetAddress(), MemoryRegionPhysicalIramSc7Firmware.GetAddress(), MemoryRegionVirtualIramSc7Firmware.GetSize(), MappingAttributesEl3NonSecureDevice); + + /* Map the TZRAM ro alias region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualTzramReadOnlyAlias.GetAddress(), MemoryRegionPhysicalTzramReadOnlyAlias.GetAddress(), MemoryRegionVirtualTzramReadOnlyAlias.GetSize(), MappingAttributesEl3SecureRoData); + + /* Map the DRAM secure data store region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualDramSecureDataStore.GetAddress(), MemoryRegionPhysicalDramSecureDataStore.GetAddress(), MemoryRegionVirtualDramSecureDataStore.GetSize(), MappingAttributesEl3NonSecureDevice); + + /* Map the program region as rwx. */ + SetL3BlockEntry(l3, MemoryRegionVirtualTzramProgram.GetAddress(), MemoryRegionPhysicalTzramProgram.GetAddress(), MemoryRegionVirtualTzramProgram.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Map the boot code region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualTzramBootCode.GetAddress(), MemoryRegionPhysicalTzramBootCode.GetAddress(), MemoryRegionVirtualTzramBootCode.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Map the volatile data regions regions. */ + SetL3BlockEntry(l3, MemoryRegionVirtualTzramVolatileData.GetAddress(), MemoryRegionPhysicalTzramVolatileData.GetAddress(), MemoryRegionVirtualTzramVolatileData.GetSize(), MappingAttributesEl3SecureRwData); + SetL3BlockEntry(l3, MemoryRegionVirtualTzramVolatileStack.GetAddress(), MemoryRegionPhysicalTzramVolatileStack.GetAddress(), MemoryRegionVirtualTzramVolatileStack.GetSize(), MappingAttributesEl3SecureRwData); + + /* Map the configuration data. */ + SetL3BlockEntry(l3, MemoryRegionVirtualTzramConfigurationData.GetAddress(), MemoryRegionPhysicalTzramConfigurationData.GetAddress(), MemoryRegionVirtualTzramConfigurationData.GetSize(), MappingAttributesEl3SecureRwData); + + /* Map the page tables. */ + SetL3BlockEntry(l3, util::AlignDown(MemoryRegionVirtualTzramL1PageTable.GetAddress(), PageSize), util::AlignDown(MemoryRegionPhysicalTzramL1PageTable.GetAddress(), PageSize), PageSize, MappingAttributesEl3SecureDevice); + SetL3BlockEntry(l3, MemoryRegionVirtualTzramL2L3PageTable.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), MemoryRegionVirtualTzramL2L3PageTable.GetSize(), MappingAttributesEl3SecureRwData); + } + } + + constexpr bool ValidateTzramPageTables() { + u64 l1_table[MemoryRegionPhysicalTzramL1PageTable.GetSize() / sizeof(u64)] = {}; + u64 l2_l3_table[MemoryRegionPhysicalTzramL2L3PageTable.GetSize() / sizeof(u64)] = {}; + + MakePageTablesImpl(l1_table, l2_l3_table, l2_l3_table); + + return true; + } + + static_assert(ValidateTzramPageTables()); + + } + + void MakePageTable() { + u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + MakePageTablesImpl(l1, l2_l3, l2_l3); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_package2.cpp @@ -0,0 +1,22 @@ +/* + * 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> + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_size_data.s b/exosphere2/program/source/boot/secmon_size_data.s new file mode 100644 index 000000000..0943ef943 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_size_data.s @@ -0,0 +1,26 @@ +/* + * 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/>. + */ + +.section .metadata.sizes, "ax", %progbits +.align 6 +.global __metadata__sizes +__metadata__sizes: + .quad 0xAAAAAAAAAAAAAAAA, 0xBBBBBBBBBBBBBBBB + .quad __glob_start__ + .quad __bootcode_start__ + .quad __bootcode_end__ + .quad __program_start__ + .quad 0xCCCCCCCCCCCCCCCC, 0xDDDDDDDDDDDDDDDD diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc new file mode 100644 index 000000000..1ee25f79a --- /dev/null +++ b/exosphere2/program/source/secmon_cache.inc @@ -0,0 +1,22 @@ +/* + * 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/>. + */ + +void FlushEntireDataCache(); +void InvalidateEntireDataCache(); + +void EnsureMappingConsistency(); +void EnsureMappingConsistency(uintptr_t address); +void EnsureInstructionConsistency(); \ No newline at end of file diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc new file mode 100644 index 000000000..c486c8b4d --- /dev/null +++ b/exosphere2/program/source/secmon_cache_impl.inc @@ -0,0 +1,132 @@ +/* + * 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/>. + */ + +namespace { + + ALWAYS_INLINE int FloorLog2(int v) { + return BITSIZEOF(u32) - (hw::CountLeadingZeros(static_cast<u32>(v)) + 1); + } + + ALWAYS_INLINE int CeilLog2(int v) { + const int log = FloorLog2(v); + return ((1 << log) == v) ? log : log + 1; + } + + void FlushDataCacheTo(int loc) { + for (int level = 0; level < loc; ++level) { + /* Set the selection register. */ + { + util::BitPack32 csselr = {}; + csselr.Set<hw::CsselrEl1::InD>(0); + csselr.Set<hw::CsselrEl1::Level>(level); + HW_CPU_SET_CSSELR_EL1(csselr); + } + + /* Ensure that reordering doesn't occur around this operation. */ + hw::InstructionSynchronizationBarrier(); + + /* Get ccsidr. */ + util::BitPack32 ccsidr; + HW_CPU_GET_CCSIDR_EL1(ccsidr); + + /* Get cache size id info. */ + const int num_sets = ccsidr.Get<hw::CcsidrEl1::NumSets>() + 1; + const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1; + const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 4; + + const int way_shift = 32 - FloorLog2(num_ways); + const int set_shift = line_size; + + for (int way = 0; way <= num_ways; way++) { + for (int set = 0; set <= num_sets; set++) { + const u64 value = (static_cast<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(level) << 1); + __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory"); + } + } + } + } + + void InvalidateDataCacheTo(int loc) { + for (int level = 0; level < loc; ++level) { + /* Set the selection register. */ + { + util::BitPack32 csselr = {}; + csselr.Set<hw::CsselrEl1::InD>(0); + csselr.Set<hw::CsselrEl1::Level>(level); + HW_CPU_SET_CSSELR_EL1(csselr); + } + + /* Ensure that reordering doesn't occur around this operation. */ + hw::InstructionSynchronizationBarrier(); + + /* Get ccsidr. */ + util::BitPack32 ccsidr; + HW_CPU_GET_CCSIDR_EL1(ccsidr); + + /* Get cache size id info. */ + const int num_sets = ccsidr.Get<hw::CcsidrEl1::NumSets>() + 1; + const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1; + const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 4; + + const int way_shift = 32 - FloorLog2(num_ways); + const int set_shift = line_size; + + for (int way = 0; way <= num_ways; way++) { + for (int set = 0; set <= num_sets; set++) { + const u64 value = (static_cast<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(level) << 1); + __asm__ __volatile__("dc isw, %[value]" :: [value]"r"(value) : "memory"); + } + } + } + } + +} + +void FlushEntireDataCache() { + util::BitPack32 clidr; + HW_CPU_GET_CLIDR_EL1(clidr); + FlushDataCacheTo(clidr.Get<hw::ClidrEl1::Loc>()); +} + +void InvalidateEntireDataCache() { + util::BitPack32 clidr; + HW_CPU_GET_CLIDR_EL1(clidr); + InvalidateDataCacheTo(clidr.Get<hw::ClidrEl1::Loc>()); +} + +void EnsureMappingConsistency() { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateEntireTlb(); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} + +void EnsureMappingConsistency(uintptr_t address) { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateTlb(address); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} + +void EnsureInstructionConsistency() { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateEntireInstructionCache(); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_cpu_context.cpp b/exosphere2/program/source/secmon_cpu_context.cpp new file mode 100644 index 000000000..d2185a699 --- /dev/null +++ b/exosphere2/program/source/secmon_cpu_context.cpp @@ -0,0 +1,192 @@ +/* + * 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_cpu_context.hpp" +#include "secmon_error.hpp" + +namespace ams::secmon { + + namespace { + + struct DebugRegisters { + u32 osdttrx_el1; + u32 osdtrtx_el1; + u32 mdscr_el1; + u32 oseccr_el1; + u32 mdccint_el1; + u32 dbgclaimclr_el1; + u32 dbgvcr32_el2; + u32 sder32_el3; + u32 mdcr_el2; + u32 mdcr_el3; + u32 spsr_el3; + u64 dbgbvcr_el1[12]; + u64 dbgwvcr_el1[ 8]; + }; + + struct CoreContext { + EntryContext entry_context; + bool is_on; + bool is_reset_expected; + bool is_debug_registers_saved; + DebugRegisters debug_registers; + }; + + void SaveDebugRegisters(DebugRegisters &dr) { + /* Set the OS lock; this will be unlocked by entry code. */ + HW_CPU_SET_OSLAR_EL1(1); + + /* Save general debug registers. */ + HW_CPU_GET_OSDTRRX_EL1 (dr.osdttrx_el1); + HW_CPU_GET_OSDTRTX_EL1 (dr.osdtrtx_el1); + HW_CPU_GET_MDSCR_EL1 (dr.mdscr_el1); + HW_CPU_GET_OSECCR_EL1 (dr.oseccr_el1); + HW_CPU_GET_MDCCINT_EL1 (dr.mdccint_el1); + HW_CPU_GET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); + HW_CPU_GET_DBGVCR32_EL2 (dr.dbgvcr32_el2); + HW_CPU_GET_SDER32_EL3 (dr.sder32_el3); + HW_CPU_GET_MDCR_EL2 (dr.mdcr_el2); + HW_CPU_GET_MDCR_EL3 (dr.mdcr_el3); + HW_CPU_GET_SPSR_EL3 (dr.spsr_el3); + + /* Save debug breakpoints. */ + HW_CPU_GET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); + HW_CPU_GET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); + HW_CPU_GET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); + HW_CPU_GET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); + HW_CPU_GET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); + HW_CPU_GET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); + HW_CPU_GET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); + HW_CPU_GET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); + HW_CPU_GET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); + HW_CPU_GET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); + HW_CPU_GET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); + HW_CPU_GET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); + + /* Save debug watchpoints. */ + HW_CPU_GET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); + HW_CPU_GET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); + HW_CPU_GET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); + HW_CPU_GET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); + HW_CPU_GET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); + HW_CPU_GET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); + HW_CPU_GET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); + HW_CPU_GET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); + } + + void RestoreDebugRegisters(const DebugRegisters &dr) { + /* Restore general debug registers. */ + HW_CPU_SET_OSDTRRX_EL1 (dr.osdttrx_el1); + HW_CPU_SET_OSDTRTX_EL1 (dr.osdtrtx_el1); + HW_CPU_SET_MDSCR_EL1 (dr.mdscr_el1); + HW_CPU_SET_OSECCR_EL1 (dr.oseccr_el1); + HW_CPU_SET_MDCCINT_EL1 (dr.mdccint_el1); + HW_CPU_SET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); + HW_CPU_SET_DBGVCR32_EL2 (dr.dbgvcr32_el2); + HW_CPU_SET_SDER32_EL3 (dr.sder32_el3); + HW_CPU_SET_MDCR_EL2 (dr.mdcr_el2); + HW_CPU_SET_MDCR_EL3 (dr.mdcr_el3); + HW_CPU_SET_SPSR_EL3 (dr.spsr_el3); + + /* Restore debug breakpoints. */ + HW_CPU_SET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); + HW_CPU_SET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); + HW_CPU_SET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); + HW_CPU_SET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); + HW_CPU_SET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); + HW_CPU_SET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); + HW_CPU_SET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); + HW_CPU_SET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); + HW_CPU_SET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); + HW_CPU_SET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); + HW_CPU_SET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); + HW_CPU_SET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); + + /* Restore debug watchpoints. */ + HW_CPU_SET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); + HW_CPU_SET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); + HW_CPU_SET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); + HW_CPU_SET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); + HW_CPU_SET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); + HW_CPU_SET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); + HW_CPU_SET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); + HW_CPU_SET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); + } + + constinit CoreContext g_core_contexts[NumCores] = {}; + + } + + bool IsCoreOn(int core) { + return g_core_contexts[core].is_on; + } + + void SetCoreOff() { + g_core_contexts[hw::GetCurrentCoreId()].is_on = false; + } + + bool IsResetExpected() { + return g_core_contexts[hw::GetCurrentCoreId()].is_reset_expected; + } + + void SetResetExpected(int core, bool expected) { + g_core_contexts[core].is_reset_expected = expected; + } + + void SetResetExpected(bool expected) { + SetResetExpected(hw::GetCurrentCoreId(), expected); + } + + void SetEntryContext(int core, uintptr_t address, uintptr_t arg) { + g_core_contexts[core].entry_context.pc = address; + g_core_contexts[core].entry_context.x0 = arg; + } + + void GetEntryContext(EntryContext *out) { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + const auto pc = ctx.entry_context.pc; + const auto x0 = ctx.entry_context.x0; + + if (pc == 0 || ctx.is_on) { + SetError(pkg1::ErrorInfo_InvalidCoreContext); + AMS_ABORT("Invalid core context"); + } + + ctx.entry_context = {}; + ctx.is_on = true; + + out->pc = pc; + out->x0 = x0; + } + + void SaveDebugRegisters() { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + SaveDebugRegisters(ctx.debug_registers); + ctx.is_debug_registers_saved = true; + } + + void RestoreDebugRegisters() { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + if (ctx.is_debug_registers_saved) { + RestoreDebugRegisters(ctx.debug_registers); + ctx.is_debug_registers_saved = false; + } + } + +} diff --git a/exosphere2/program/source/secmon_cpu_context.hpp b/exosphere2/program/source/secmon_cpu_context.hpp new file mode 100644 index 000000000..a19bd8af8 --- /dev/null +++ b/exosphere2/program/source/secmon_cpu_context.hpp @@ -0,0 +1,41 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + constexpr inline int NumCores = 4; + + struct EntryContext { + u64 x0; + u64 pc; + }; + + bool IsCoreOn(int core); + void SetCoreOff(); + + bool IsResetExpected(); + void SetResetExpected(int core, bool expected); + void SetResetExpected(bool expected); + + void SetEntryContext(int core, uintptr_t address, uintptr_t arg); + void GetEntryContext(EntryContext *out); + + void SaveDebugRegisters(); + void RestoreDebugRegisters(); + +} diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp new file mode 100644 index 000000000..68338212d --- /dev/null +++ b/exosphere2/program/source/secmon_error.cpp @@ -0,0 +1,51 @@ +/* + * 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_error.hpp" + +namespace ams::diag { + + void AbortImpl() { + secmon::SetError(pkg1::ErrorInfo_UnknownAbort); + secmon::ErrorReboot(); + } + +} + +namespace ams::secmon { + + void SetError(pkg1::ErrorInfo info) { + const uintptr_t address = secmon::MemoryRegionVirtualDevicePmc.GetAddress() + PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH; + + if (reg::Read(address) == pkg1::ErrorInfo_None) { + reg::Write(address, info); + } + } + + NORETURN void ErrorReboot() { + /* Lockout the security engine. */ + se::Lockout(); + + /* TODO: Lockout fuses. */ + + /* TODO: Disable SE Crypto Operations. */ + + while (true) { + wdt::Reboot(); + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_error.hpp b/exosphere2/program/source/secmon_error.hpp new file mode 100644 index 000000000..abbfad245 --- /dev/null +++ b/exosphere2/program/source/secmon_error.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + void SetError(pkg1::ErrorInfo info); + NORETURN void ErrorReboot(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere2/program/source/secmon_exception_vectors.s new file mode 100644 index 000000000..ef1ab9f32 --- /dev/null +++ b/exosphere2/program/source/secmon_exception_vectors.s @@ -0,0 +1,326 @@ +/* + * 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/>. + */ + +/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */ +/* + * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Declare the exception vector table, enforcing it is aligned on a + * 2KB boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_base label, section_name=.vectors +.section \section_name, "ax" +.align 11, 0 +\label: +.endm + +/* + * Create an entry in the exception vector table, enforcing it is + * aligned on a 128-byte boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_entry label, section_name=.vectors +.cfi_sections .debug_frame +.section \section_name, "ax" +.align 7, 0 +.type \label, %function +.func \label +.cfi_startproc +\label: +.endm + +/* + * This macro verifies that the given vector doesnt exceed the + * architectural limit of 32 instructions. This is meant to be placed + * immediately after the last instruction in the vector. It takes the + * vector entry as the parameter + */ +.macro check_vector_size since + .endfunc + .cfi_endproc + .if (. - \since) > (32 * 4) + .error "Vector exceeds 32 instructions" + .endif +.endm + +/* Actual Vectors for Secure Monitor. */ +.global _ZN3ams6secmon16ExceptionVectorsEv +vector_base _ZN3ams6secmon16ExceptionVectorsEv + +/* Current EL, SP0 */ +vector_entry synch_sp0 + /* Branch to the exception handler. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon26UnexpectedExceptionHandlerEv: + /* Load the ErrorInfo scratch. */ + ldr x0, =0x1F004AC40 + + /* Write ErrorInfo_UnknownAbort to it. */ + ldr w1, =0x07F00010 + str w1, [x0] + + /* Perform an error reboot. */ + b _ZN3ams6secmon11ErrorRebootEv + +vector_entry irq_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_sp0 + +vector_entry fiq_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size fiq_sp0 + +vector_entry serror_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_sp0 + +/* Current EL, SPx */ +vector_entry synch_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size synch_spx + +vector_entry irq_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_spx + +vector_entry fiq_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size fiq_spx + +vector_entry serror_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_spx + +/* Lower EL, A64 */ +vector_entry synch_a64 + /* Check whether the exception is an SMC. If it's not, take the unexpected handler. */ + stp x29, x30, [sp, #-0x10]! + mrs x30, esr_el3 + lsr w29, w30, #26 + cmp w29, #0x17 + ldp x29, x30, [sp], #0x10 + b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv + + /* Save x29 and x30. */ + stp x29, x30, [sp, #-0x10]! + + /* Get the current core. */ + mrs x29, mpidr_el1 + and x29, x29, #3 + + /* If we're not on core 3, take the core0-2 handler. */ + cmp x29, #3 + b.ne _ZN3ams6secmon25HandleSmcExceptionCore012Ev + + /* Handle the smc. */ + bl _ZN3ams6secmon18HandleSmcExceptionEv + + /* Return. */ + ldp x29, x30, [sp], #0x10 + eret + check_vector_size synch_a64 + +vector_entry irq_a64 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_a64 + +vector_entry fiq_a64 + /* Save X29, X30. */ + stp x29, x30, [sp, #-0x10]! + + /* Get the current core ID, ensure it's core 3. */ + mrs x29, mpidr_el1 + and x29, x29, #3 + cmp x29, #3 + b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv + + /* Save x26-x28, x18. */ + stp x28, x18, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + + /* Set x18 to the global data region. */ + ldr x18, =0x1F01FA000 + + /* Handle the fiq exception. */ + bl _ZN3ams6secmon18HandleFiqExceptionEv + + /* Restore registers. */ + ldp x26, x27, [sp], #0x10 + ldp x28, x18, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + /* Return. */ + eret + check_vector_size fiq_a64 + +vector_entry serror_a64 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon25HandleSmcExceptionCore012Ev: + /* Acquire exclusive access to the common smc stack. */ + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + bl _ZN3ams6secmon25AcquireCommonSmcStackLockEv + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + + /* Pivot to use the common smc stack. */ + mov x30, sp + ldr x29, =0x1F01F6E80 + mov sp, x29 + stp x29, x30, [sp, #-0x10]! + + /* Handle the SMC. */ + bl _ZN3ams6secmon18HandleSmcExceptionEv + + /* Restore our core-specific stack. */ + ldp x29, x30, [sp], #0x10 + mov x30, sp + + /* Release our exclusive access to the common smc stack. */ + stp x0, x1, [sp, #-0x10]! + bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv + ldp x0, x1, [sp], #0x10 + + /* Return. */ + ldp x29, x30, [sp], #0x10 + eret + +/* Lower EL, A32 */ +vector_entry synch_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size synch_a32 + +vector_entry irq_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon18HandleSmcExceptionEv: + /* Save registers. */ + stp x29, x30, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + + /* Set x18 to the global data region. */ + ldr x18, =0x1F01FA000 + + /* Get esr. */ + mrs x0, esr_el3 + and x0, x0, #0xFFFF + + /* Get the function arguments. */ + mov x1, sp + + /* Invoke the smc handler. */ + bl _ZN3ams6secmon3smc9HandleSmcEiRNS1_12SmcArgumentsE + + /* Restore registers. */ + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + ret +vector_entry fiq_a32 + /* Handle fiq from a32 the same as fiq from a64. */ + b fiq_a64 + .endfunc + .cfi_endproc +_ZN3ams6secmon18HandleFiqExceptionEv: + /* Save registers. */ + stp x29, x30, [sp, #-0x10]! + stp x24, x25, [sp, #-0x10]! + stp x22, x23, [sp, #-0x10]! + stp x20, x21, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + + /* Invoke the interrupt handler. */ + bl _ZN3ams6secmon15HandleInterruptEv + + /* Restore registers. */ + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + ret + +vector_entry serror_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_a32 + + /* Instantiate the literal pool for the exception vectors. */ + .ltorg diff --git a/exosphere2/program/source/secmon_interrupt_handler.cpp b/exosphere2/program/source/secmon_interrupt_handler.cpp new file mode 100644 index 000000000..41f466632 --- /dev/null +++ b/exosphere2/program/source/secmon_interrupt_handler.cpp @@ -0,0 +1,64 @@ +/* + * 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_interrupt_handler.hpp" +#include "secmon_error.hpp" + +namespace ams::secmon { + + namespace { + + constexpr inline int InterruptHandlersMax = 4; + + constinit InterruptHandler g_handlers[InterruptHandlersMax] = {}; + constinit int g_interrupt_ids[InterruptHandlersMax] = {}; + + } + + void SetInterruptHandler(int interrupt_id, InterruptHandler handler) { + for (int i = 0; i < InterruptHandlersMax; ++i) { + if (g_interrupt_ids[i] == 0) { + g_interrupt_ids[i] = interrupt_id; + g_handlers[i] = handler; + return; + } + } + + AMS_ABORT("Failed to register interrupt handler"); + } + + void HandleInterrupt() { + /* Get the interrupt id. */ + const int interrupt_id = gic::GetInterruptRequestId(); + if (interrupt_id >= gic::InterruptCount) { + /* Invalid interrupt number, just return. */ + return; + } + + /* Check each handler. */ + for (int i = 0; i < InterruptHandlersMax; ++i) { + if (g_interrupt_ids[i] == interrupt_id) { + /* Invoke the handler. */ + g_handlers[i](); + gic::SetEndOfInterrupt(interrupt_id); + return; + } + } + + AMS_ABORT("Failed to find interrupt handler."); + } + +} diff --git a/exosphere2/program/source/secmon_interrupt_handler.hpp b/exosphere2/program/source/secmon_interrupt_handler.hpp new file mode 100644 index 000000000..136da0264 --- /dev/null +++ b/exosphere2/program/source/secmon_interrupt_handler.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + using InterruptHandler = void (*)(); + + void SetInterruptHandler(int interrupt_id, InterruptHandler handler); + +} diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp new file mode 100644 index 000000000..8e823deec --- /dev/null +++ b/exosphere2/program/source/secmon_key_storage.cpp @@ -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_key_storage.hpp" + +namespace ams::secmon { + + namespace { + + constinit u8 g_rsa_moduli[ImportRsaKey_Count][se::RsaSize] = {}; + constinit bool g_rsa_modulus_committed[ImportRsaKey_Count] = {}; + + ALWAYS_INLINE u8 *GetRsaKeyModulus(ImportRsaKey which) { + return g_rsa_moduli[which]; + } + + ALWAYS_INLINE u8 *GetRsaKeyPrivateExponent(ImportRsaKey which) { + return ::ams::secmon::impl::GetRsaPrivateExponentStorage(static_cast<int>(which)); + } + + ALWAYS_INLINE bool IsRsaKeyProvisional(ImportRsaKey which) { + return g_rsa_modulus_committed[which] == false; + } + + void ClearRsaKeyModulus(ImportRsaKey which) { + g_rsa_modulus_committed[which] = false; + std::memset(g_rsa_moduli[which], 0, se::RsaSize); + } + + ALWAYS_INLINE u8 *GetMasterKeyStorage(int index) { + return ::ams::secmon::impl::GetMasterKeyStorage(index); + } + + ALWAYS_INLINE u8 *GetDeviceMasterKeyStorage(int index) { + return ::ams::secmon::impl::GetDeviceMasterKeyStorage(index); + } + + } + + void ImportRsaKeyExponent(ImportRsaKey which, const void *src, size_t size) { + /* If we import an exponent, the modulus is not committed. */ + ClearRsaKeyModulus(which); + + /* Copy the exponent. */ + std::memcpy(GetRsaKeyPrivateExponent(which), src, size); + } + + void ImportRsaKeyModulusProvisionally(ImportRsaKey which, const void *src, size_t size) { + std::memcpy(GetRsaKeyModulus(which), src, std::min(static_cast<int>(size), se::RsaSize)); + } + + void CommitRsaKeyModulus(ImportRsaKey which) { + g_rsa_modulus_committed[which] = true; + } + + bool LoadRsaKey(int slot, ImportRsaKey which) { + /* If the key is still provisional, we can't load it. */ + if (IsRsaKeyProvisional(which)) { + return false; + } + + se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize); + return true; + } + + void LoadProvisionalRsaKey(int slot, ImportRsaKey which) { + se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize); + } + + void SetMasterKey(int generation, const void *src, size_t size) { + const int index = generation - pkg1::KeyGeneration_Min; + se::EncryptAes128(GetMasterKeyStorage(index), se::AesBlockSize, pkg1::AesKeySlot_RandomForKeyStorageWrap, src, size); + } + + void LoadMasterKey(int slot, int generation) { + const int index = std::min(0, generation - pkg1::KeyGeneration_Min); + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetMasterKeyStorage(index), se::AesBlockSize); + } + + void SetDeviceMasterKey(int generation, const void *src, size_t size) { + const int index = generation - pkg1::KeyGeneration_4_0_0; + se::EncryptAes128(GetDeviceMasterKeyStorage(index), se::AesBlockSize, pkg1::AesKeySlot_RandomForKeyStorageWrap, src, size); + } + + void LoadDeviceMasterKey(int slot, int generation) { + const int index = std::min(0, generation - pkg1::KeyGeneration_4_0_0); + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetDeviceMasterKeyStorage(index), se::AesBlockSize); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp new file mode 100644 index 000000000..39d34c9da --- /dev/null +++ b/exosphere2/program/source/secmon_key_storage.hpp @@ -0,0 +1,44 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + enum ImportRsaKey { + ImportRsaKey_EsDrmCert = 0, + ImportRsaKey_Lotus = 1, + ImportRsaKey_Ssl = 2, + ImportRsaKey_EsClientCert = 3, + + ImportRsaKey_Count = 4, + }; + static_assert(util::size(secmon::ConfigurationContext{}.rsa_private_exponents) == ImportRsaKey_Count); + + void ImportRsaKeyExponent(ImportRsaKey which, const void *src, size_t size); + void ImportRsaKeyModulusProvisionally(ImportRsaKey which, const void *src, size_t size); + void CommitRsaKeyModulus(ImportRsaKey which); + + bool LoadRsaKey(int slot, ImportRsaKey which); + void LoadProvisionalRsaKey(int slot, ImportRsaKey which); + + void SetMasterKey(int generation, const void *src, size_t size); + void LoadMasterKey(int slot, int generation); + + void SetDeviceMasterKey(int generation, const void *src, size_t size); + void LoadDeviceMasterKey(int slot, int generation); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp new file mode 100644 index 000000000..8f7a1c500 --- /dev/null +++ b/exosphere2/program/source/secmon_misc.cpp @@ -0,0 +1,46 @@ +/* + * 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_misc.hpp" + +namespace ams::secmon { + + namespace { + + pkg1::BctParameters g_bct_params; + + } + + void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params) { + /* Save the BCT parameters. */ + g_bct_params = secmon_params.bct_params; + } + + bool IsRecoveryBoot() { + return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RecoveryBoot) != 0; + } + + u32 GetRestrictedSmcMask() { + return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RestrictedSmcMask) >> pkg1::BootloaderAttribute_RestrictedSmcShift; + } + + bool IsJtagEnabled() { + util::BitPack32 dbg_auth; + HW_CPU_GET_DBGAUTHSTATUS_EL1(dbg_auth); + return dbg_auth.Get<hw::DbgAuthStatusEl1::Nsid>() == 3; + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp new file mode 100644 index 000000000..b26641919 --- /dev/null +++ b/exosphere2/program/source/secmon_misc.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params); + + bool IsRecoveryBoot(); + + u32 GetRestrictedSmcMask(); + + bool IsJtagEnabled(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp new file mode 100644 index 000000000..a468d6dce --- /dev/null +++ b/exosphere2/program/source/secmon_setup.cpp @@ -0,0 +1,750 @@ +/* + * 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_setup.hpp" +#include "secmon_error.hpp" +#include "secmon_cpu_context.hpp" +#include "secmon_interrupt_handler.hpp" + +namespace ams::secmon { + + namespace { + + constexpr inline const uintptr_t TIMER = secmon::MemoryRegionVirtualDeviceTimer.GetAddress(); + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionVirtualDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress(); + + alignas(8) constinit u8 g_se_aes_key_slot_test_vector[se::AesBlockSize] = {}; + + struct Carveout { + uintptr_t address; + size_t size; + }; + + constinit Carveout g_kernel_carveouts[KernelCarveoutCount] = { + { secmon::MemoryRegionDramDefaultKernelCarveout.GetAddress(), secmon::MemoryRegionDramDefaultKernelCarveout.GetSize(), }, + { 0, 0, }, + }; + + constinit bool g_is_cold_boot = true; + + constinit const se::StickyBits ExpectedSeStickyBits = { + .se_security = (1 << 0), /* SE_HARD_SETTING */ + .tzram_security = 0, + .crypto_security_perkey = 0, + .crypto_keytable_access = { + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 2: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 6: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 7: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 14: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + }, + .rsa_security_perkey = 0, + .rsa_keytable_access = { + (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */ + (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */ + }, + }; + + void InitializeConfigurationContext() { + /* Get the global context. */ + auto &ctx = ::ams::secmon::impl::GetConfigurationContext(); + + /* Clear the context to zero. */ + std::memset(std::addressof(ctx), 0, sizeof(ctx)); + + /* If the storage context is valid, we want to copy it to the global context. */ + if (const auto &storage_ctx = *MemoryRegionPhysicalDramMonitorConfiguration.GetPointer<const SecureMonitorStorageConfiguration>(); storage_ctx.IsValid()) { + ctx.secmon_cfg.CopyFrom(storage_ctx); + ctx.emummc_cfg = storage_ctx.emummc_cfg; + } else { + /* If we don't have a valid storage context, we can just use the default one. */ + ctx.secmon_cfg = DefaultSecureMonitorConfiguration; + } + } + + void GenerateSecurityEngineAesKeySlotTestVector(void *dst, size_t size) { + /* Clear the output. */ + AMS_ABORT_UNLESS(size == se::AesBlockSize); + std::memset(dst, 0, se::AesBlockSize); + + /* Ensure output is seen as cleared by the se. */ + hw::FlushDataCache(dst, se::AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Declare a block. */ + alignas(8) u8 empty_block[se::AesBlockSize]; + + /* Iteratively transform an empty block. */ + #define TRANSFORM_WITH_KEY(key) \ + __builtin_memset(empty_block, 0, sizeof(empty_block)); \ + se::SetEncryptedAesKey256(pkg1::AesKeySlot_Temporary, key, empty_block, sizeof(empty_block)); \ + se::DecryptAes128(dst, se::AesBlockSize, pkg1::AesKeySlot_Temporary, dst, se::AesBlockSize) + + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device); + + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device); + + /* Ensure output is seen correctly by the cpu. */ + hw::FlushDataCache(dst, se::AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Clear the temporary key slot. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); + } + + void VerifySecurityEngineStickyBits() { + if (!se::ValidateStickyBits(ExpectedSeStickyBits)) { + SetError(pkg1::ErrorInfo_InvalidSecurityEngineStickyBits); + AMS_ABORT("Invalid sticky bits"); + } + } + + void VerifySecurityEngineAesKeySlotTestVector() { + alignas(8) u8 test_vector[se::AesBlockSize]; + GenerateSecurityEngineAesKeySlotTestVector(test_vector, sizeof(test_vector)); + + AMS_ABORT_UNLESS(crypto::IsSameBytes(g_se_aes_key_slot_test_vector, test_vector, se::AesBlockSize)); + } + + void ClearAesKeySlots() { + /* Clear all non-secure monitor keys. */ + for (int i = 0; i < pkg1::AesKeySlot_SecmonStart; ++i) { + se::ClearAesKeySlot(i); + } + + /* Clear the secure-monitor temporary keys. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); + se::ClearAesKeySlot(pkg1::AesKeySlot_Smc); + } + + void ClearRsaKeySlots() { + /* Clear all rsa keyslots. */ + for (int i = 0; i < se::RsaKeySlotCount; ++i) { + se::ClearRsaKeySlot(i); + } + } + + void SetupKernelCarveouts() { + #define MC_ENABLE_CLIENT_ACCESS(INDEX, WHICH) MC_REG_BITS_ENUM(CLIENT_ACCESS##INDEX##_##WHICH, ENABLE) + + constexpr u32 ClientAccess0 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(0, PTCR), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0A), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0AB), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0B), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0BB), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0C), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0CB), + MC_ENABLE_CLIENT_ACCESS(0, AFIR), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHC), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHCB), + MC_ENABLE_CLIENT_ACCESS(0, HDAR), + MC_ENABLE_CLIENT_ACCESS(0, HOST1XDMAR), + MC_ENABLE_CLIENT_ACCESS(0, HOST1XR), + MC_ENABLE_CLIENT_ACCESS(0, NVENCSRD), + MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBDMAR), + MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBSLVR)); + + constexpr u32 ClientAccess1 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(1, MPCORER), + MC_ENABLE_CLIENT_ACCESS(1, NVENCSWR), + MC_ENABLE_CLIENT_ACCESS(1, AFIW), + MC_ENABLE_CLIENT_ACCESS(1, HDAW), + MC_ENABLE_CLIENT_ACCESS(1, HOST1XW), + MC_ENABLE_CLIENT_ACCESS(1, MPCOREW), + MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBDMAW), + MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBSLVW)); + + constexpr u32 ClientAccess2 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW)); + + constexpr u32 ClientAccess2_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW), + MC_ENABLE_CLIENT_ACCESS(2, TSECSRD), + MC_ENABLE_CLIENT_ACCESS(2, TSECSWR)); + + constexpr u32 ClientAccess3 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB), + MC_ENABLE_CLIENT_ACCESS(3, VICSRD), + MC_ENABLE_CLIENT_ACCESS(3, VICSWR), + MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD), + MC_ENABLE_CLIENT_ACCESS(3, APER), + MC_ENABLE_CLIENT_ACCESS(3, APEW), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR)); + + constexpr u32 ClientAccess3_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB), + MC_ENABLE_CLIENT_ACCESS(3, VICSRD), + MC_ENABLE_CLIENT_ACCESS(3, VICSWR), + MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD), + MC_ENABLE_CLIENT_ACCESS(3, NVDECSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVDECSWR), + MC_ENABLE_CLIENT_ACCESS(3, APER), + MC_ENABLE_CLIENT_ACCESS(3, APEW), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR)); + + constexpr u32 ClientAccess4 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR)); + + constexpr u32 ClientAccess4_800 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR), + MC_ENABLE_CLIENT_ACCESS(4, TSECRDB), + MC_ENABLE_CLIENT_ACCESS(4, TSECWRB)); + + + constexpr u32 ClientAccess4_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR)); + + #undef MC_ENABLE_CLIENT_ACCESS + + constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); + constexpr u32 ForceInternalAccess0_100 = 0; + + constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + constexpr u32 ForceInternalAccess1_100 = 0; + + constexpr u32 CarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + constexpr u32 CarveoutConfig_100 = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + const u32 target_fw = GetTargetFirmware(); + u32 client_access_2; + u32 client_access_3; + u32 client_access_4; + u32 carveout_config; + if (target_fw >= TargetFirmware_8_1_0) { + client_access_2 = ClientAccess2; + client_access_3 = ClientAccess3; + client_access_4 = ClientAccess4; + carveout_config = CarveoutConfig; + } else if (target_fw >= TargetFirmware_8_0_0) { + client_access_2 = ClientAccess2; + client_access_3 = ClientAccess3; + client_access_4 = ClientAccess4_800; + carveout_config = CarveoutConfig; + } else { + client_access_2 = ClientAccess2_100; + client_access_3 = ClientAccess3_100; + client_access_4 = ClientAccess4_100; + carveout_config = CarveoutConfig_100; + } + + /* Configure carveout 4. */ + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, g_kernel_carveouts[0].address >> 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, g_kernel_carveouts[0].address >> 32); + reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, ClientAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, ClientAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, client_access_2); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, client_access_3); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, client_access_4); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess0 : ForceInternalAccess0_100); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess1 : ForceInternalAccess1_100); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CFG0, carveout_config); + + /* Configure carveout 5. */ + reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM, g_kernel_carveouts[0].address >> 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM_HI, g_kernel_carveouts[0].address >> 32); + reg::Write(MC + MC_SECURITY_CARVEOUT5_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0, ClientAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1, ClientAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2, client_access_2); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3, client_access_3); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4, client_access_4); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CFG0, carveout_config); + } + + void ConfigureSlaveSecurity() { + u32 reg0, reg1, reg2; + if (GetTargetFirmware() > TargetFirmware_1_0_0) { + reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SE, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE)); + + reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE)); + + reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, DP2, ENABLE)); + + const auto hw_type = fuse::GetHardwareType(); + + /* Switch Lite can't use HDMI, so set CEC to secure on hoag. */ + if (hw_type == fuse::HardwareType_Hoag) { + reg0 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, CEC, ENABLE)); + } + + /* Icosa, Iowa, and Five all set I2C4 to be secure. */ + if (hw_type == fuse::HardwareType_Icosa && hw_type == fuse::HardwareType_Iowa && hw_type == fuse::HardwareType_Five) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, I2C4, ENABLE)); + + } + + /* Hoag additionally sets UART_B to secure. */ + if (hw_type == fuse::HardwareType_Hoag) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE)); + } + + /* Copper and Calcio lack a lot of hardware, so set the corresponding registers to secure for them. */ + if (hw_type == fuse::HardwareType_Calcio || hw_type == fuse::HardwareType_Copper) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, UART_C, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI4, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C3, ENABLE)); + + /* Copper/Calcio have no gamecard reader, and thus set SDMMC2 as secure. */ + reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC2, ENABLE)); + } + + /* Mariko hardware types (not Icosa or Copper) additionally set mariko-only mmio (SE2, PKA1, FEK) as secure. */ + if (hw_type != fuse::HardwareType_Icosa && hw_type != fuse::HardwareType_Copper) { + reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SE2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, PKA1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, FEK, ENABLE)); + } + } else { + reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE)); + + reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE)); + + reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE), + REG_BITS_VALUE(5, 1, 1), /* Note: Bit 5 is not documented in TRM. */ + REG_BITS_VALUE(4, 1, 1)); /* Note: Bit 4 is not documented in TRM. */ + } + + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, reg0); + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, reg1); + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0, reg2); + } + + void SetupSecureRegisters() { + /* Configure timers 5-8 and watchdog timers 0-3 as secure. */ + reg::Write(TIMER + TIMER_SHARED_TIMER_SECURE_CFG, TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, ENABLE)); + + /* Lock cluster switching, to prevent usage of the A53 cores. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, ENABLE), + FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, DISABLE), + FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, FAST)); + + /* Enable flow controller debug qualifier for legacy FIQs. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_FLOW_DBG_QUAL, FLOW_REG_BITS_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, ENABLE)); + + /* Configure the PMC to disable deep power-down. */ + reg::Write(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_TSC_MULT_EN, DISABLE), + PMC_REG_BITS_ENUM(DPD_ENABLE_ON, DISABLE)); + + /* Configure the video protect region. */ + reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_0, 1); + reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_1, 0); + reg::Write(MC + MC_VIDEO_PROTECT_BOM, 0); + reg::Write(MC + MC_VIDEO_PROTECT_SIZE_MB, 0); + reg::Write(MC + MC_VIDEO_PROTECT_REG_CTRL, MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, DISABLED), + MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, DISABLED)); + + /* Configure the SEC carveout. */ + reg::Write(MC + MC_SEC_CARVEOUT_BOM, 0); + reg::Write(MC + MC_SEC_CARVEOUT_SIZE_MB, 0); + reg::Write(MC + MC_SEC_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, DISABLED)); + + /* Configure the MTS carveout. */ + reg::Write(MC + MC_MTS_CARVEOUT_BOM, 0); + reg::Write(MC + MC_MTS_CARVEOUT_SIZE_MB, 0); + reg::Write(MC + MC_MTS_CARVEOUT_ADR_HI, 0); + reg::Write(MC + MC_MTS_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, DISABLED)); + + /* Configure the security carveout. */ + reg::Write(MC + MC_SECURITY_CFG0, MC_REG_BITS_VALUE(SECURITY_CFG0_SECURITY_BOM, 0)); + reg::Write(MC + MC_SECURITY_CFG1, MC_REG_BITS_VALUE(SECURITY_CFG1_SECURITY_SIZE, 0)); + reg::Write(MC + MC_SECURITY_CFG3, MC_REG_BITS_VALUE(SECURITY_CFG3_SECURITY_BOM_HI, 3)); + + /* Configure security carveout 1. */ + reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_SIZE_128KB, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + + /* Security carveout 2 will be configured later by SetupGpuCarveout, after magic values are written to configure gpu/tsec. */ + + /* Configure carveout 3. */ + reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_SIZE_128KB, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2, MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSRD, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSWR, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4, MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSRD2, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSWR2, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 3), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + + /* Configure the two kernel carveouts. */ + SetupKernelCarveouts(); + + /* Configure slave register security. */ + ConfigureSlaveSecurity(); + } + + void SetupSmmu() { + /* Turn on SMMU translation for all devices. */ + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_0, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_1, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_2, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_3, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_4, ~0u); + + /* Configure ASIDs 1-3 as secure, and all others as non-secure. */ + reg::Write(MC + MC_SMMU_ASID_SECURITY, MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, SECURE)); + + reg::Write(MC + MC_SMMU_ASID_SECURITY_1, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_2, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_3, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_4, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_5, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_6, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_7, 0); + + /* Initialize the PTB registers to zero .*/ + reg::Write(MC + MC_SMMU_PTB_ASID, 0); + reg::Write(MC + MC_SMMU_PTB_DATA, 0); + + /* Configure the TLB and PTC, then read TLB_CONFIG to ensure configuration takes. */ + reg::Write(MC + MC_SMMU_TLB_CONFIG, MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, ENABLE), + MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, ENABLE), + MC_REG_BITS_VALUE(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0x30)); + + reg::Write(MC + MC_SMMU_PTC_CONFIG, MC_REG_BITS_ENUM (SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, ENABLE), + MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 8), + MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0x3F)); + + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Enable the SMMU, and read TLB_CONFIG to ensure the enable takes. */ + reg::Write(MC + MC_SMMU_CONFIG, MC_REG_BITS_ENUM (SMMU_CONFIG_SMMU_ENABLE, ENABLE)); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + } + + } + + void Setup1() { + /* Load the global configuration context. */ + InitializeConfigurationContext(); + + /* Initialize uart for logging. */ + log::Initialize(); + + /* Initialize the security engine. */ + se::Initialize(); + + /* Initialize the gic. */ + gic::InitializeCommon(); + } + + void SaveSecurityEngineAesKeySlotTestVector() { + GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector)); + } + + void SetupSocSecurity() { + /* Set the fuse visibility. */ + clkrst::SetFuseVisibility(true); + + /* Set fuses as only secure-writable. */ + fuse::SetWriteSecureOnly(); + + /* Lockout the fuses. */ + fuse::Lockout(); + + /* Set the security engine to secure mode. */ + se::SetSecure(true); + + /* Verify the security engine's sticky bits. */ + VerifySecurityEngineStickyBits(); + + /* Verify the security engine's Aes slots contain correct contents. */ + VerifySecurityEngineAesKeySlotTestVector(); + + /* Clear aes keyslots. */ + ClearAesKeySlots(); + + /* Clear rsa keyslots. */ + ClearRsaKeySlots(); + + /* Overwrite keys that we want to be random with random contents. */ + se::InitializeRandom(); + se::SetRandomKey(pkg1::AesKeySlot_Temporary); + se::GenerateSrk(); + se::SetRandomKey(pkg1::AesKeySlot_TzramSave); + + /* Initialize pmc secure scratch. */ + pmc::InitializeRandomScratch(); + + /* Setup secure registers. */ + SetupSecureRegisters(); + + /* Setup the smmu. */ + SetupSmmu(); + + /* Clear the cpu reset vector. */ + reg::Write(secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress() + EVP_CPU_RESET_VECTOR, 0); + + /* Configure the SB registers to our start address. */ + constexpr u32 ResetVectorLow = static_cast<u32>((PhysicalTzramProgramResetVector >> 0)); + constexpr u32 ResetVectorHigh = static_cast<u32>((PhysicalTzramProgramResetVector >> BITSIZEOF(u32))); + + /* Write our reset vector to the secure boot registers. */ + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_LOW, ResetVectorLow); + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_HIGH, ResetVectorHigh); + + /* Disable non-secure writes to the reset vector. */ + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR, SB_REG_BITS_ENUM(CSR_NS_RST_VEC_WR_DIS, DISABLE)); + + /* Read back SB_CSR to make sure our non-secure write disable takes. */ + reg::Read(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR); + + /* Write our reset vector to scratch registers used by warmboot, and lock those scratch registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH34, ResetVectorLow); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH35, ResetVectorHigh); + pmc::LockSecureRegister(pmc::SecureRegister_ResetVector); + + /* Setup the security engine interrupt. */ + constexpr int SecurityEngineInterruptId = 90; + gic::SetPriority (SecurityEngineInterruptId, gic::HighestPriority); + gic::SetInterruptGroup(SecurityEngineInterruptId, 0); + gic::SetEnable (SecurityEngineInterruptId, true); + gic::SetSpiTargetCpu (SecurityEngineInterruptId, (1 << 3)); + gic::SetSpiMode (SecurityEngineInterruptId, gic::InterruptMode_Level); + + /* Setup the activity monitor interrupt. */ + constexpr int ActivityMonitorInterruptId = 77; + gic::SetPriority (ActivityMonitorInterruptId, gic::HighestPriority); + gic::SetInterruptGroup(ActivityMonitorInterruptId, 0); + gic::SetEnable (ActivityMonitorInterruptId, true); + gic::SetSpiTargetCpu (ActivityMonitorInterruptId, (1 << 3)); + gic::SetSpiMode (ActivityMonitorInterruptId, gic::InterruptMode_Level); + + /* If we're coldboot, perform one-time setup. */ + if (g_is_cold_boot) { + /* Register both interrupt handlers. */ + SetInterruptHandler(SecurityEngineInterruptId, se::HandleInterrupt); + SetInterruptHandler(ActivityMonitorInterruptId, actmon::HandleInterrupt); + + /* We're expecting the other cores to come out of reset. */ + for (int i = 1; i < NumCores; ++i) { + SetResetExpected(i, true); + } + + /* We only coldboot once. */ + g_is_cold_boot = false; + } + } + + void SetupSocProtections() { + + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp new file mode 100644 index 000000000..4113c7905 --- /dev/null +++ b/exosphere2/program/source/secmon_setup.hpp @@ -0,0 +1,36 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + constexpr inline u64 MemoryAttributeIndexNormal = 0; + constexpr inline u64 MemoryAttributeIndexDevice = 1; + + constexpr inline int KernelCarveoutCount = 2; + + void SetupCpuMemoryControllersEnableMmu(); + + void SetupSocDmaControllers(); + void SetupSocSecurity(); + void SetupSocProtections(); + + void Setup1(); + + void SaveSecurityEngineAesKeySlotTestVector(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp new file mode 100644 index 000000000..1f168d89a --- /dev/null +++ b/exosphere2/program/source/secmon_setup_warm.cpp @@ -0,0 +1,206 @@ +/* + * 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_setup.hpp" + +namespace ams::secmon { + + namespace setup { + + #include "secmon_cache_impl.inc" + + } + + namespace { + + using namespace ams::mmu; + + void SetupCpuCommonControllers() { + /* Set cpuactlr_el1. */ + { + util::BitPack64 cpuactlr = {}; + cpuactlr.Set<hw::CpuactlrEl1CortexA57::NonCacheableStreamingEnhancement>(1); + cpuactlr.Set<hw::CpuactlrEl1CortexA57::DisableLoadPassDmb>(1); + HW_CPU_SET_CPUACTLR_EL1(cpuactlr); + } + + /* Set cpuectlr_el1. */ + { + util::BitPack64 cpuectlr = {}; + cpuectlr.Set<hw::CpuectlrEl1CortexA57::Smpen>(1); + cpuectlr.Set<hw::CpuectlrEl1CortexA57::L2LoadStoreDataPrefetchDistance>(3); + cpuectlr.Set<hw::CpuectlrEl1CortexA57::L2InstructionFetchPrefetchDistance>(3); + HW_CPU_SET_CPUECTLR_EL1(cpuectlr); + } + + /* Prevent instruction reordering. */ + hw::InstructionSynchronizationBarrier(); + } + + void SetupCpuEl3Controllers() { + /* Set scr_el3. */ + { + util::BitPack32 scr = {}; + scr.Set<hw::ScrEl3::Ns >(1); /* Set EL0/EL1 as Non-Secure. */ + scr.Set<hw::ScrEl3::Irq >(0); /* IRQs are taken in IRQ mode. */ + scr.Set<hw::ScrEl3::Fiq >(1); /* FIQs are taken in Monitor mode. */ + scr.Set<hw::ScrEl3::Ea >(1); /* External aborts are taken in Monitor mode. */ + scr.Set<hw::ScrEl3::Fw >(1); /* CPSR.F is non-secure writable. */ + scr.Set<hw::ScrEl3::Aw >(1); /* CPSR.A is non-secure writable. */ + scr.Set<hw::ScrEl3::Net >(0); /* This bit is not implemented. */ + scr.Set<hw::ScrEl3::Smd >(0); /* Secure Monitor Call is allowed. */ + scr.Set<hw::ScrEl3::Hce >(0); /* Hypervisor Calls are disabled. */ /* TODO: Enable for thermosphere? */ + scr.Set<hw::ScrEl3::Sif >(1); /* Secure mode cannot fetch instructions from non-secure memory. */ + scr.Set<hw::ScrEl3::RwCortexA53>(1); /* Reserved bit. N probably sets it because on Cortex A53, this sets kernel as aarch64. */ + scr.Set<hw::ScrEl3::StCortexA53>(0); /* Reserved bit. On Cortex A53, this sets secure registers to EL3 only. */ + scr.Set<hw::ScrEl3::Twi >(0); /* WFI is not trapped. */ + scr.Set<hw::ScrEl3::Twe >(0); /* WFE is not trapped. */ + + HW_CPU_SET_SCR_EL3(scr); + } + + /* Set ttbr0_el3. */ + { + constexpr u64 ttbr0 = MemoryRegionPhysicalTzramL1PageTable.GetAddress(); + HW_CPU_SET_TTBR0_EL3(ttbr0); + } + + /* Set tcr_el3. */ + { + util::BitPack32 tcr = { hw::TcrEl3::Res1 }; + tcr.Set<hw::TcrEl3::T0sz >(31); /* Configure TTBR0 addressed size to be 64 GiB */ + tcr.Set<hw::TcrEl3::Irgn0>(1); /* Configure PTE walks as inner write-back write-allocate cacheable */ + tcr.Set<hw::TcrEl3::Orgn0>(1); /* Configure PTE walks as outer write-back write-allocate cacheable */ + tcr.Set<hw::TcrEl3::Sh0 >(3); /* Configure PTE walks as inner shareable */ + tcr.Set<hw::TcrEl3::Tg0 >(0); /* Set TTBR0_EL3 granule as 4 KiB */ + tcr.Set<hw::TcrEl3::Ps >(1); /* Set the physical addrss size as 36-bit (64 GiB) */ + tcr.Set<hw::TcrEl3::Tbi >(0); /* Top byte is not ignored in addrss calculations */ + + HW_CPU_SET_TCR_EL3(tcr); + } + + /* Clear cptr_el3. */ + { + util::BitPack32 cptr = {}; + + cptr.Set<hw::CptrEl3::Tfp >(0); /* FP/SIMD instructions don't trap. */ + cptr.Set<hw::CptrEl3::Tta >(0); /* Reserved bit (no trace functionality present). */ + cptr.Set<hw::CptrEl3::Tcpac>(0); /* Access to cpacr_El1 does not trap. */ + + HW_CPU_SET_CPTR_EL3(cptr); + } + + /* Set mair_el3. */ + { + u64 mair = (MemoryRegionAttributes_Normal << (MemoryRegionAttributeWidth * MemoryAttributeIndexNormal)) | + (MemoryRegionAttributes_Device << (MemoryRegionAttributeWidth * MemoryAttributeIndexDevice)); + HW_CPU_SET_MAIR_EL3(mair); + } + + /* Set vectors. */ + { + constexpr u64 vectors = MemoryRegionVirtualTzramProgramExceptionVectors.GetAddress(); + HW_CPU_SET_VBAR_EL3(vectors); + } + + /* Prevent instruction re-ordering around this point. */ + hw::InstructionSynchronizationBarrier(); + } + + void EnableMmu() { + /* Create sctlr value. */ + util::BitPack32 sctlr = { hw::SctlrEl3::Res1 }; + sctlr.Set<hw::SctlrEl3::M>(1); /* Globally enable the MMU. */ + sctlr.Set<hw::SctlrEl3::A>(0); /* Disable alignment fault checking. */ + sctlr.Set<hw::SctlrEl3::C>(1); /* Globally enable the data and unified caches. */ + sctlr.Set<hw::SctlrEl3::Sa>(0); /* Disable stack alignment checking. */ + sctlr.Set<hw::SctlrEl3::I>(1); /* Globally enable the instruction cache. */ + sctlr.Set<hw::SctlrEl3::Wxn>(0); /* Do not force writable pages to be ExecuteNever. */ + sctlr.Set<hw::SctlrEl3::Ee>(0); /* Exceptions should be little endian. */ + + /* Ensure all writes are done before turning on the mmu. */ + hw::DataSynchronizationBarrierInnerShareable(); + + /* Invalidate the entire tlb. */ + hw::InvalidateEntireTlb(); + + /* Ensure instruction consistency. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::InstructionSynchronizationBarrier(); + + /* Set sctlr_el3. */ + HW_CPU_SET_SCTLR_EL3(sctlr); + hw::InstructionSynchronizationBarrier(); + } + + } + + void SetupCpuMemoryControllersEnableMmu() { + SetupCpuCommonControllers(); + SetupCpuEl3Controllers(); + EnableMmu(); + } + + void SetupSocDmaControllers() { + /* Ensure that our caches are managed. */ + setup::InvalidateEntireDataCache(); + setup::EnsureInstructionConsistency(); + + /* Lock tsec. */ + tsec::Lock(); + + /* Enable SWID[0] for all bits. */ + reg::Write(AHB_ARBC(AHB_MASTER_SWID), ~0u); + + /* Clear SWID1 for all bits. */ + reg::Write(AHB_ARBC(AHB_MASTER_SWID_1), 0u); + + /* Set MSELECT config to set WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ + /* and clear ERR_RESP_EN_SLAVE1(PCIe) | ERR_RESP_EN_SLAVE2(GPU) */ + { + auto mselect_cfg = reg::Read(MSELECT(MSELECT_CONFIG)); + mselect_cfg &= ~(1u << 24); /* Clear ERR_RESP_EN_SLAVE1(PCIe) */ + mselect_cfg &= ~(1u << 25); /* Clear ERR_RESP_EN_SLAVE2(GPU) */ + mselect_cfg |= (1u << 27); /* Set WRAP_TO_INCR_SLAVE0(APC) */ + mselect_cfg |= (1u << 28); /* Set WRAP_TO_INCR_SLAVE1(PCIe) */ + mselect_cfg |= (1u << 29); /* Set WRAP_TO_INCR_SLAVE2(GPU) */ + reg::Write(MSELECT(MSELECT_CONFIG), mselect_cfg); + } + + /* Disable USB, USB2, AHB-DMA from arbitration. */ + { + auto arb_dis = reg::Read(AHB_ARBC(AHB_ARBITRATION_DISABLE)); + arb_dis |= (1u << 5); /* Disable AHB-DMA */ + arb_dis |= (1u << 6); /* Disable USB */ + arb_dis |= (1u << 18); /* Disable USB2 */ + reg::Write(AHB_ARBC(AHB_ARBITRATION_DISABLE), arb_dis); + } + + /* Select high priority group with priority 7. */ + { + u32 priority_ctrl = {}; + priority_ctrl |= (7u << 29); /* Set group 7. */ + priority_ctrl |= (1u << 0); /* Set high priority. */ + reg::Write(AHB_ARBC(AHB_ARBITRATION_PRIORITY_CTRL), priority_ctrl); + } + + /* Prevent splitting AHB writes to TZRAM. */ + { + reg::Write(AHB_ARBC(AHB_GIZMO_TZRAM), (1u << 7)); + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s new file mode 100644 index 000000000..e71744dad --- /dev/null +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -0,0 +1,83 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams6secmon5StartEv, "ax", %progbits +.align 6 +.global _ZN3ams6secmon5StartEv +_ZN3ams6secmon5StartEv: + /* Set SPSEL 1 stack pointer to the core 0 exception stack address. */ + msr spsel, #1 + ldr x20, =0x1F01F6F00 + mov sp, x20 + + /* Set SPSEL 0 stack pointer to a temporary location in volatile memory. */ + msr spsel, #1 + ldr x20, =0x1F01C0800 + mov sp, x20 + + /* Setup X18 to point to the global context. */ + ldr x18, =0x1F01FA000 + + /* Invoke main. */ + bl _ZN3ams6secmon4MainEv + + /* Set the stack pointer to the core 3 exception stack address. */ + ldr x20, =0x1F01FB000 + mov sp, x20 + + /* TODO: JumpToLowerExceptionLevel */ + 1: b 1b + +.section .text._ZN3ams6secmon25AcquireCommonSmcStackLockEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon25AcquireCommonSmcStackLockEv +_ZN3ams6secmon25AcquireCommonSmcStackLockEv: + /* Get the address of the lock. */ + ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE + + /* Prepare to try to take the spinlock. */ + mov w1, #1 + sevl + prfm pstl1keep, [x0] + +1: /* Repeatedly try to take the lock. */ + wfe + ldaxr w2, [x0] + cbnz w2, 1b + stxr w2, w1, [x0] + cbnz w2, 1b + + /* Return. */ + ret + +.section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon25ReleaseCommonSmcStackLockEv +_ZN3ams6secmon25ReleaseCommonSmcStackLockEv: + /* Get the address of the lock. */ + ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE + + /* Release the spinlock. */ + stlr wzr, [x0] + + /* Return. */ + ret + +.section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits +.global _ZN3ams6secmon18CommonSmcStackLockE +_ZN3ams6secmon18CommonSmcStackLockE: + /* Define storage for the global common smc stack spinlock. */ + .word 0 diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp new file mode 100644 index 000000000..9283a278c --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -0,0 +1,52 @@ +/* + * 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_error.hpp" +#include "secmon_smc_aes.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateAesKek(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcLoadAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcComputeAes(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcComputeCmac(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcLoadPreparedAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp new file mode 100644 index 000000000..b0b3c3d95 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateAesKek(const SmcArguments &args); + SmcResult SmcLoadAesKey(const SmcArguments &args); + SmcResult SmcComputeAes(const SmcArguments &args); + SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args); + SmcResult SmcComputeCmac(const SmcArguments &args); + SmcResult SmcLoadPreparedAesKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp new file mode 100644 index 000000000..ba037d498 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_carveout.cpp @@ -0,0 +1,27 @@ +/* + * 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_error.hpp" +#include "secmon_smc_carveout.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere2/program/source/smc/secmon_smc_carveout.hpp new file mode 100644 index 000000000..f3d346d97 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_carveout.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp new file mode 100644 index 000000000..dba7c4af5 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_common.hpp @@ -0,0 +1,45 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon::smc { + + enum class SmcResult : u32 { + Success = 0, + NotImplemented = 1, + InvalidArgument = 2, + InProgress = 3, + NoAsyncOperation = 4, + InvalidAsyncOperation = 5, + NotPermitted = 6, + NotInitialized = 7, + + PsciSuccess = 0, + PsciNotSupported = static_cast<u32>(-1), + PsciInvalidParameters = static_cast<u32>(-2), + PsciDenied = static_cast<u32>(-3), + PsciAlreadyOn = static_cast<u32>(-4), + }; + + struct SmcArguments { + u64 r[8]; + }; + + constexpr inline int SecurityEngineUserInterruptId = 44; + constexpr inline u64 InvalidAsyncKey = 0; + +} diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp new file mode 100644 index 000000000..39fb28dcb --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -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_error.hpp" +#include "secmon_smc_device_unique_data.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + /* Legacy APIs. */ + SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp new file mode 100644 index 000000000..9def74026 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args); + SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args); + + /* Legacy APIs. */ + SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args); + SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere2/program/source/smc/secmon_smc_error.cpp new file mode 100644 index 000000000..309a2e832 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_error.cpp @@ -0,0 +1,27 @@ +/* + * 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_error.hpp" +#include "secmon_smc_error.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere2/program/source/smc/secmon_smc_error.hpp new file mode 100644 index 000000000..62eff45da --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_error.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp new file mode 100644 index 000000000..a707c35f0 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_es.cpp @@ -0,0 +1,32 @@ +/* + * 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_error.hpp" +#include "secmon_smc_es.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcPrepareEsCommonKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/exosphere2/program/source/smc/secmon_smc_es.hpp new file mode 100644 index 000000000..1aabbc210 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_es.hpp @@ -0,0 +1,32 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + enum EsKeyType { + EsKeyType_TitleKey = 0, + EsKeyType_ArchiveKey = 1, + + EsKeyType_Count = 2, + }; + + SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args); + SmcResult SmcPrepareEsCommonKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp new file mode 100644 index 000000000..75bfa4357 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -0,0 +1,233 @@ +/* + * 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_error.hpp" +#include "../secmon_misc.hpp" +#include "secmon_smc_common.hpp" +#include "secmon_smc_handler.hpp" +#include "secmon_smc_aes.hpp" +#include "secmon_smc_carveout.hpp" +#include "secmon_smc_device_unique_data.hpp" +#include "secmon_smc_error.hpp" +#include "secmon_smc_es.hpp" +#include "secmon_smc_info.hpp" +#include "secmon_smc_memory_access.hpp" +#include "secmon_smc_power_management.hpp" +#include "secmon_smc_random.hpp" +#include "secmon_smc_register_access.hpp" +#include "secmon_smc_result.hpp" +#include "secmon_smc_rsa.hpp" + +namespace ams::secmon::smc { + + namespace { + + struct HandlerInfo { + u32 function_id; + u32 restriction_mask; + SmcHandler handler; + }; + + struct HandlerTable { + const HandlerInfo *entries; + size_t count; + }; + + enum HandlerType : int { + HandlerType_User = 0, + HandlerType_Kern = 1, + HandlerType_Count = 2, + }; + + enum Restriction { + Restriction_None = (0 << 0), + Restriction_Normal = (1 << 0), + Restriction_DeviceUniqueDataNotAllowed = (1 << 1), + Restriction_SafeModeNotAllowed = (1 << 2), + }; + + enum SmcCallRange { + SmcCallRange_ArmArch = 0, + SmcCallRange_Cpu = 1, + SmcCallRange_Sip = 2, + SmcCallRange_Oem = 3, + SmcCallRange_Standard = 4, + + SmcCallRange_TrustedApp = 0x30, + }; + + enum SmcArgumentType { + ArgumentType_Integer = 0, + ArgumentType_Pointer = 1, + }; + + enum SmcConvention { + Convention_Smc32 = 0, + Convention_Smc64 = 1, + }; + + enum SmcCallType { + SmcCallType_YieldingCall = 0, + SmcCallType_FastCall = 1, + }; + + struct SmcFunctionId { + using FunctionId = util::BitPack64::Field< 0, 8, u32>; + using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>; + using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>; + using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>; + using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>; + using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>; + using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>; + using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>; + using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>; + using Reserved = util::BitPack64::Field<16, 8, u32>; + using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>; + using Convention = util::BitPack64::Field<30, 1, SmcConvention>; + using CallType = util::BitPack64::Field<31, 1, SmcCallType>; + using Reserved2 = util::BitPack64::Field<32, 32, u32>; + }; + + constinit HandlerInfo g_user_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig }, + { 0xC3000002, Restriction_Normal, SmcGetConfigUser }, + { 0xC3000003, Restriction_Normal, SmcGetResult }, + { 0xC3000404, Restriction_Normal, SmcGetResultData }, + { 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate }, + { 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes }, + { 0xC3000007, Restriction_Normal, SmcGenerateAesKek }, + { 0xC3000008, Restriction_Normal, SmcLoadAesKey }, + { 0xC3000009, Restriction_Normal, SmcComputeAes }, + { 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey }, + { 0xC300040B, Restriction_Normal, SmcComputeCmac }, + { 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData }, + { 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData }, + { 0xC300000E, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey }, + { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey }, + { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey }, + { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonKey } + }; + + constinit HandlerInfo g_kern_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, + { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu }, + { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu }, + { 0xC3000002, Restriction_Normal, SmcGetConfigKern }, + { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking }, + { 0xC3000006, Restriction_Normal, SmcShowError }, + { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, + { 0xC3000008, Restriction_Normal, SmcReadWriteRegister }, + }; + + constinit HandlerInfo g_ams_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xF0000201, Restriction_None, SmcIramCopy }, + { 0xF0000002, Restriction_None, SmcReadWriteRegister }, + { 0xF0000003, Restriction_None, SmcWriteAddress }, + { 0xF0000003, Restriction_None, SmcGetEmummcConfig }, + }; + + constinit HandlerTable g_handler_tables[] = { + { g_user_handlers, util::size(g_user_handlers) }, + { g_kern_handlers, util::size(g_kern_handlers) }, + }; + + constinit HandlerTable g_ams_handler_table = { + g_ams_handlers, util::size(g_ams_handlers) + }; + + NORETURN void InvalidSmcError(u64 id) { + SetError(pkg1::ErrorInfo_UnknownSmc); + AMS_ABORT("Invalid SMC: %lx", id); + } + + const HandlerTable &GetHandlerTable(HandlerType type, u64 id) { + /* Ensure we have a valid handler type. */ + if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) { + InvalidSmcError(id); + } + + /* Check if we're a user SMC. */ + if (type == HandlerType_User) { + /* Nintendo uses OEM SMCs. */ + /* We will assign Atmosphere extension SMCs the TrustedApplication range. */ + if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) { + return g_ams_handler_table; + } + + /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */ + if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) { + InvalidSmcError(id); + } + } + + return g_handler_tables[type]; + } + + const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { + /* Get and check the index. */ + const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>(); + if (AMS_UNLIKELY(index >= table.count)) { + InvalidSmcError(id); + } + + /* Get and check the handler info. */ + const auto &handler_info = table.entries[index]; + + /* Check that the handler isn't null. */ + if (AMS_UNLIKELY(handler_info.handler == nullptr)) { + InvalidSmcError(id); + } + + /* Check that the handler's id matches. */ + if (AMS_UNLIKELY(handler_info.function_id != id)) { + InvalidSmcError(id); + } + + return handler_info; + } + + bool IsHandlerRestricted(const HandlerInfo &info) { + return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0; + } + + SmcResult InvokeSmcHandler(const HandlerInfo &info, const SmcArguments &args) { + /* Check if the smc is restricted. */ + if (AMS_UNLIKELY(IsHandlerRestricted(info))) { + return SmcResult::NotPermitted; + } + + /* Invoke the smc. */ + return info.handler(args); + } + + } + + void HandleSmc(int type, SmcArguments &args) { + /* Get the table. */ + const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]); + + /* Get the handler info. */ + const auto &info = GetHandlerInfo(table, args.r[0]); + + /* Set the invocation result. */ + args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args)); + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere2/program/source/smc/secmon_smc_handler.hpp new file mode 100644 index 000000000..9c5d4b317 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_handler.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using SmcHandler = SmcResult (*)(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp new file mode 100644 index 000000000..2be199d28 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -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_error.hpp" +#include "secmon_smc_info.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGetConfigUser(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGetConfigKern(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcSetConfig(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp new file mode 100644 index 000000000..1d41950ff --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -0,0 +1,59 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + enum class ConfigItem : u32 { + /* Standard config items. */ + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineIrqNumber = 3, + FuseVersion = 4, + HardwareType = 5, + IsRetail = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDebugMode = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + IsQuest = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, + Package2Hash = 17, + + /* Extension config items for exosphere. */ + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + }; + + SmcResult SmcGetConfigUser(const SmcArguments &args); + SmcResult SmcGetConfigKern(const SmcArguments &args); + SmcResult SmcSetConfig(const SmcArguments &args); + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp new file mode 100644 index 000000000..47f29fc8d --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -0,0 +1,33 @@ +/* + * 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_error.hpp" +#include "secmon_smc_memory_access.hpp" + +namespace ams::secmon::smc { + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcWriteAddress(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp new file mode 100644 index 000000000..2b44fe292 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(const SmcArguments &args); + SmcResult SmcWriteAddress(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp new file mode 100644 index 000000000..7434bf217 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -0,0 +1,37 @@ +/* + * 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_error.hpp" +#include "secmon_smc_power_management.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPowerOffCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcPowerOnCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcSuspendCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp new file mode 100644 index 000000000..7b67d6e78 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPowerOffCpu(const SmcArguments &args); + SmcResult SmcPowerOnCpu(const SmcArguments &args); + + SmcResult SmcSuspendCpu(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp new file mode 100644 index 000000000..283e8d736 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -0,0 +1,32 @@ +/* + * 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_error.hpp" +#include "secmon_smc_random.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateRandomBytes(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere2/program/source/smc/secmon_smc_random.hpp new file mode 100644 index 000000000..27b20b241 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_random.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateRandomBytes(const SmcArguments &args); + SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp new file mode 100644 index 000000000..a656174f3 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -0,0 +1,27 @@ +/* + * 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_error.hpp" +#include "secmon_smc_register_access.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcReadWriteRegister(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere2/program/source/smc/secmon_smc_register_access.hpp new file mode 100644 index 000000000..d364b27f8 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_register_access.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcReadWriteRegister(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp new file mode 100644 index 000000000..6b17b8e56 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -0,0 +1,32 @@ +/* + * 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_error.hpp" +#include "secmon_smc_result.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGetResult(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGetResultData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere2/program/source/smc/secmon_smc_result.hpp new file mode 100644 index 000000000..9355f22af --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_result.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size); + + u64 BeginAsyncOperation(GetResultHandler handler); + void CancelAsyncOperation(u64 async_key); + void EndAsyncOperation(); + + SmcResult SmcGetResult(const SmcArguments &args); + SmcResult SmcGetResultData(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp new file mode 100644 index 000000000..78c98e620 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -0,0 +1,32 @@ +/* + * 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_error.hpp" +#include "secmon_smc_rsa.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcModularExponentiate(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp new file mode 100644 index 000000000..c2dcd5cbe --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcModularExponentiate(const SmcArguments &args); + SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/split_program.py b/exosphere2/program/split_program.py new file mode 100644 index 000000000..b52406eba --- /dev/null +++ b/exosphere2/program/split_program.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import sys, lz4 +from struct import unpack as up + +def lz4_compress(data): + return lz4.block.compress(data, 'high_compression', store_size=False) + +def split_binary(data): + A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('<QQQQQQQQ', data[:0x40]) + assert A == 0xAAAAAAAAAAAAAAAA + assert B == 0xBBBBBBBBBBBBBBBB + assert C == 0xCCCCCCCCCCCCCCCC + assert D == 0xDDDDDDDDDDDDDDDD + data = data[0x40:] + + boot_code = data[BOOT_CODE_START - START:BOOT_CODE_END - BOOT_CODE_START] + program = data[PROGRAM_START - START:] + return [('boot_code.lz4', lz4_compress(boot_code)), ('program.lz4', lz4_compress(program))] + +def main(argc, argv): + if argc != 3: + print 'Usage: %s in outdir' % argv[0] + return 1 + with open(argv[1], 'rb') as f: + data = f.read() + assert len(data) >= 0x40 + for (fn, fdata) in split_binary(data): + with open('%s/%s' % (argv[2], fn), 'wb') as f: + f.write(fdata) + return 0 + +if __name__ == '__main__': + sys.exit(main(len(sys.argv), sys.argv)) \ No newline at end of file diff --git a/libraries/config/arch/arm/arch.mk b/libraries/config/arch/arm/arch.mk new file mode 100644 index 000000000..f629bb39f --- /dev/null +++ b/libraries/config/arch/arm/arch.mk @@ -0,0 +1,11 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro") +endif + +include $(DEVKITPRO)/devkitARM/base_rules + +export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM +export ATMOSPHERE_SETTINGS += +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += diff --git a/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk new file mode 100644 index 000000000..d50dd0607 --- /dev/null +++ b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk @@ -0,0 +1,5 @@ +export ATMOSPHERE_DEFINES += -DATMOSPHERE_CPU_ARM7TDMI +export ATMOSPHERE_SETTINGS += -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += \ No newline at end of file diff --git a/libraries/config/board/nintendo/nx_bpmp/board.mk b/libraries/config/board/nintendo/nx_bpmp/board.mk new file mode 100644 index 000000000..e267e24d6 --- /dev/null +++ b/libraries/config/board/nintendo/nx_bpmp/board.mk @@ -0,0 +1,5 @@ +export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_NX -D__BPMP__ +export ATMOSPHERE_SETTINGS += +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += \ No newline at end of file diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 4d68b232e..e3a12dd7b 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -8,12 +8,13 @@ export ATMOSPHERE_LIBRARIES_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/.. ifeq ($(strip $(ATMOSPHERE_BOARD)),) export ATMOSPHERE_BOARD := nx-hac-001 -endif ifeq ($(strip $(ATMOSPHERE_CPU)),) export ATMOSPHERE_CPU := arm-cortex-a57 endif +endif + export ATMOSPHERE_DEFINES := -DATMOSPHERE export ATMOSPHERE_SETTINGS := -fPIE -g export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ @@ -23,6 +24,8 @@ export ATMOSPHERE_ASFLAGS := ifeq ($(ATMOSPHERE_BOARD),nx-hac-001) + +ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) export ATMOSPHERE_ARCH_DIR := arch/arm64 export ATMOSPHERE_BOARD_DIR := board/nintendo/nx export ATMOSPHERE_OS_DIR := os/horizon @@ -30,6 +33,16 @@ export ATMOSPHERE_OS_DIR := os/horizon export ATMOSPHERE_ARCH_NAME := arm64 export ATMOSPHERE_BOARD_NAME := nintendo_nx export ATMOSPHERE_OS_NAME := horizon +else ifeq ($(ATMOSPHERE_CPU),arm7tdmi) +export ATMOSPHERE_ARCH_DIR := arch/arm +export ATMOSPHERE_BOARD_DIR := board/nintendo/nx_bpmp +export ATMOSPHERE_OS_DIR := os/horizon + +export ATMOSPHERE_ARCH_NAME := arm +export ATMOSPHERE_BOARD_NAME := nintendo_nx +export ATMOSPHERE_OS_NAME := horizon +endif + endif ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) @@ -37,12 +50,20 @@ export ATMOSPHERE_CPU_DIR := arch/arm64/cpu/cortex_a57 export ATMOSPHERE_CPU_NAME := arm_cortex_a57 endif +ifeq ($(ATMOSPHERE_CPU),arm7tdmi) +export ATMOSPHERE_CPU_DIR := arch/arm/cpu/arm7tdmi +export ATMOSPHERE_CPU_NAME := arm7tdmi +endif + export ATMOSPHERE_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_ARCH_DIR) export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_BOARD_DIR) export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_OS_DIR) export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_CPU_DIR) +export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) +export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) + include $(ATMOSPHERE_ARCH_MAKE_DIR)/arch.mk include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk new file mode 100644 index 000000000..163626659 --- /dev/null +++ b/libraries/config/templates/exosphere.mk @@ -0,0 +1,46 @@ +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm) +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +endif + +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now + +export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ + -Wl,--wrap,__cxa_throw \ + -Wl,--wrap,__cxa_rethrow \ + -Wl,--wrap,__cxa_allocate_exception \ + -Wl,--wrap,__cxa_free_exception \ + -Wl,--wrap,__cxa_begin_catch \ + -Wl,--wrap,__cxa_end_catch \ + -Wl,--wrap,__cxa_call_unexpected \ + -Wl,--wrap,__cxa_call_terminate \ + -Wl,--wrap,__gxx_personality_v0 \ + -Wl,--wrap,_Unwind_Resume \ + -Wl,--wrap,_ZSt19__throw_logic_errorPKc \ + -Wl,--wrap,_ZSt20__throw_length_errorPKc \ + -Wl,--wrap,_ZNSt11logic_errorC2EPKc + +export LIBS := -lexosphere + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere diff --git a/libraries/libexosphere/Makefile b/libraries/libexosphere/Makefile new file mode 100644 index 000000000..97b463c6b --- /dev/null +++ b/libraries/libexosphere/Makefile @@ -0,0 +1,19 @@ +.PHONY: all arm64 arm clean arm64-clean arm-clean + +all: arm64 arm + +arm64: + $(MAKE) -f arm64.mk + +arm: + $(MAKE) -f arm.mk + +#--------------------------------------------------------------------------------- + +clean: arm64-clean arm-clean + +arm64-clean: + $(MAKE) -f arm64.mk clean + +arm-clean: + $(MAKE) -f arm.mk clean diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk new file mode 100644 index 000000000..7a653f53b --- /dev/null +++ b/libraries/libexosphere/arm.mk @@ -0,0 +1,142 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) + +SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours + +#--------------------------------------------------------------------------------- +# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +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. + +.PHONY: clean all + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a + +$(ATMOSPHERE_LIBRARY_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(CURDIR)/arm.mk + +dist-bin: all + @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) + +dist-src: + @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm.mk + +dist: dist-src dist-bin + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES) : $(GCH_FILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +libc.o: CFLAGS += -fno-builtin + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk new file mode 100644 index 000000000..c9a17e60d --- /dev/null +++ b/libraries/libexosphere/arm64.mk @@ -0,0 +1,143 @@ +#--------------------------------------------------------------------------------- +# 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))))/../config/common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) + +SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours + +#--------------------------------------------------------------------------------- +# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +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. + +.PHONY: clean all + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a + +$(ATMOSPHERE_LIBRARY_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2" \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(CURDIR)/arm64.mk + +dist-bin: all + @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) + +dist-src: + @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm64.mk + +dist: dist-src dist-bin + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES) : $(GCH_FILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +libc.o: CFLAGS += -fno-builtin + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp new file mode 100644 index 000000000..db9b2cb2d --- /dev/null +++ b/libraries/libexosphere/include/exosphere.hpp @@ -0,0 +1,38 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#include <exosphere/common.hpp> +#include <exosphere/reg.hpp> +#include <exosphere/hw.hpp> +#include <exosphere/util.hpp> +#include <exosphere/mmu.hpp> +#include <exosphere/gic.hpp> +#include <exosphere/wdt.hpp> +#include <exosphere/pkg1.hpp> +#include <exosphere/tsec.hpp> +#include <exosphere/se.hpp> +#include <exosphere/fuse.hpp> +#include <exosphere/i2c.hpp> +#include <exosphere/uart.hpp> +#include <exosphere/pmic.hpp> +#include <exosphere/log.hpp> +#include <exosphere/clkrst.hpp> +#include <exosphere/actmon.hpp> +#include <exosphere/pmc.hpp> +#include <exosphere/secmon.hpp> +#include <exosphere/tegra.hpp> \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/actmon.hpp b/libraries/libexosphere/include/exosphere/actmon.hpp new file mode 100644 index 000000000..929b32849 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/actmon.hpp @@ -0,0 +1,30 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::actmon { + + using InterruptHandler = void(*)(); + + void SetRegisterAddress(uintptr_t address); + + void HandleInterrupt(); + + void StartMonitoringBpmp(InterruptHandler handler); + void StopMonitoringBpmp(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp new file mode 100644 index 000000000..c26dfb118 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::clkrst { + + void SetRegisterAddress(uintptr_t address); + + void SetFuseVisibility(bool visible); + + void EnableUartAClock(); + void EnableUartBClock(); + void EnableUartCClock(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/common.hpp b/libraries/libexosphere/include/exosphere/common.hpp new file mode 100644 index 000000000..c0f4e4b72 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/common.hpp @@ -0,0 +1,30 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#if 1 || defined(AMS_BUILD_FOR_AUDITING) +#define EXOSPHERE_BUILD_FOR_AUDITING +#endif + +#if defined(EXOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) +#define EXOSPHERE_BUILD_FOR_DEBUGGING +#endif + +#ifdef EXOSPHERE_BUILD_FOR_DEBUGGING +#define EXOSPHERE_ENABLE_ASSERTIONS +#define EXOSPHERE_ENABLE_DEBUG_PRINT +#endif diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp new file mode 100644 index 000000000..3df44b8a3 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -0,0 +1,49 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/pmic.hpp> + +namespace ams::fuse { + + enum HardwareType { + HardwareType_Icosa = 0, + HardwareType_Copper = 1, + HardwareType_Hoag = 2, + HardwareType_Iowa = 3, + HardwareType_Calcio = 4, + HardwareType_Five = 5, + + HardwareType_Undefined = 0xF, + }; + + enum HardwareState { + HardwareState_Development = 0, + HardwareState_Production = 1, + HardwareState_Undefined = 2, + }; + + void SetRegisterAddress(uintptr_t address); + void SetWriteSecureOnly(); + void Lockout(); + + u32 GetOdmWord(int index); + + HardwareType GetHardwareType(); + HardwareState GetHardwareState(); + pmic::Regulator GetRegulator(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/gic.hpp b/libraries/libexosphere/include/exosphere/gic.hpp new file mode 100644 index 000000000..72e30432c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/gic.hpp @@ -0,0 +1,42 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::gic { + + enum InterruptMode { + InterruptMode_Level = 0, + InterruptMode_Edge = 1, + }; + + constexpr inline s32 HighestPriority = 0; + constexpr inline s32 InterruptCount = 224; + + void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address); + void InitializeCommon(); + void InitializeCoreUnique(); + + void SetPriority(int interrupt_id, int priority); + void SetInterruptGroup(int interrupt_id, int group); + void SetEnable(int interrupt_id, bool enable); + void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask); + void SetSpiMode(int interrupt_id, InterruptMode mode); + + int GetInterruptRequestId(); + void SetEndOfInterrupt(int interrupt_id); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw.hpp b/libraries/libexosphere/include/exosphere/hw.hpp new file mode 100644 index 000000000..73c68b050 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include <exosphere/hw/hw_arm64.hpp> + namespace ams::hw { using namespace ams::hw::arch::arm64; } +#elif defined(ATMOSPHERE_ARCH_ARM) + #include <exosphere/hw/hw_arm.hpp> + namespace ams::hw { using namespace ams::hw::arch::arm; } +#else + #error "Unknown architecture for hw!" +#endif \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp new file mode 100644 index 000000000..344e30726 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp @@ -0,0 +1,49 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::hw::arch::arm { + +#ifdef __BPMP__ + constexpr inline size_t DataCacheLineSize = 0x1; + + ALWAYS_INLINE void DataSynchronizationBarrier() { + /* ... */ + } + + ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() { + /* ... */ + } + + ALWAYS_INLINE void DataMemoryBarrier() { + /* ... */ + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + /* ... */ + } + + ALWAYS_INLINE void FlushDataCache(const void *ptr, size_t size) { + AMS_UNUSED(ptr); + AMS_UNUSED(size); + /* ... */ + } +#else + #error "Unknown ARM board for ams::hw" +#endif + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp new file mode 100644 index 000000000..404a3c5b7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp @@ -0,0 +1,69 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/hw/hw_arm64_system_registers.hpp> +#include <exosphere/hw/hw_arm64_cache.hpp> + +namespace ams::hw::arch::arm64 { + + ALWAYS_INLINE void DataSynchronizationBarrier() { + __asm__ __volatile__("dsb sy" ::: "memory"); + } + + ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() { + __asm__ __volatile__("dsb ish" ::: "memory"); + } + + ALWAYS_INLINE void DataMemoryBarrier() { + __asm__ __volatile__("dmb sy" ::: "memory"); + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + __asm__ __volatile__("isb" ::: "memory"); + } + + ALWAYS_INLINE void WaitForInterrupt() { + __asm__ __volatile__("wfi" ::: "memory"); + } + + ALWAYS_INLINE void WaitForEvent() { + __asm__ __volatile__("wfe" ::: "memory"); + } + + ALWAYS_INLINE void SendEvent() { + __asm__ __volatile__("sev" ::: "memory"); + } + + ALWAYS_INLINE int CountLeadingZeros(u64 v) { + u64 z; + __asm__ __volatile__("clz %[z], %[v]" : [z]"=r"(z) : [v]"r"(v)); + return z; + } + + ALWAYS_INLINE int CountLeadingZeros(u32 v) { + u32 z; + __asm__ __volatile__("clz %w[z], %w[v]" : [z]"=r"(z) : [v]"r"(v)); + return z; + } + + ALWAYS_INLINE int GetCurrentCoreId() { + u64 mpidr; + HW_CPU_GET_MPIDR_EL1(mpidr); + return mpidr & 0xFF; + } + +} diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp new file mode 100644 index 000000000..ac534fe14 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp @@ -0,0 +1,55 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::hw::arch::arm64 { + +#if defined(ATMOSPHERE_CPU_ARM_CORTEX_A57) || defined(ATMOSPHERE_CPU_ARM_CORTEX_A53) + constexpr inline size_t InstructionCacheLineSize = 0x40; + constexpr inline size_t DataCacheLineSize = 0x40; + constexpr inline size_t NumPerformanceCounters = 6; +#else + #error "Unknown CPU for cache line sizes" +#endif + + ALWAYS_INLINE void InvalidateEntireTlb() { + __asm__ __volatile__("tlbi alle3is" ::: "memory"); + } + + ALWAYS_INLINE void InvalidateDataCacheLine(void *ptr) { + __asm __volatile__("dc ivac, %[ptr]" :: [ptr]"r"(ptr) : "memory"); + } + + ALWAYS_INLINE void FlushDataCacheLine(void *ptr) { + __asm __volatile__("dc civac, %[ptr]" :: [ptr]"r"(ptr) : "memory"); + } + + ALWAYS_INLINE void InvalidateEntireInstructionCache() { + __asm__ __volatile__("ic iallu" ::: "memory"); + } + + ALWAYS_INLINE void InvalidateTlb(uintptr_t address) { + __asm__ __volatile__("tlbi vae3is, %[address]" :: [address]"r"(address) : "memory"); + } + + ALWAYS_INLINE void InvalidateTlbLastLevel(uintptr_t address) { + __asm__ __volatile__("tlbi vale3is, %[address]" :: [address]"r"(address) : "memory"); + } + + void FlushDataCache(const void *ptr, size_t size); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp new file mode 100644 index 000000000..637f3c026 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp @@ -0,0 +1,245 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::hw::arch::arm64 { + + #define HW_CPU_GET_SYSREG(name, value) __asm__ __volatile__("mrs %0, " #name "" : "=&r"(value) :: "memory"); + + #define HW_CPU_SET_SYSREG(name, value) __asm__ __volatile__("msr " #name ", %0" :: "r"(value) : "memory", "cc") + + #define HW_CPU_GET_SCTLR_EL3(value) HW_CPU_GET_SYSREG(sctlr_el3, value) + #define HW_CPU_SET_SCTLR_EL3(value) HW_CPU_SET_SYSREG(sctlr_el3, value) + + #define HW_CPU_GET_SCR_EL3(value) HW_CPU_GET_SYSREG(scr_el3, value) + #define HW_CPU_SET_SCR_EL3(value) HW_CPU_SET_SYSREG(scr_el3, value) + + #define HW_CPU_GET_CPTR_EL3(value) HW_CPU_GET_SYSREG(cptr_el3, value) + #define HW_CPU_SET_CPTR_EL3(value) HW_CPU_SET_SYSREG(cptr_el3, value) + + #define HW_CPU_GET_TTBR0_EL3(value) HW_CPU_GET_SYSREG(ttbr0_el3, value) + #define HW_CPU_SET_TTBR0_EL3(value) HW_CPU_SET_SYSREG(ttbr0_el3, value) + + #define HW_CPU_GET_TCR_EL3(value) HW_CPU_GET_SYSREG(tcr_el3, value) + #define HW_CPU_SET_TCR_EL3(value) HW_CPU_SET_SYSREG(tcr_el3, value) + + #define HW_CPU_GET_MAIR_EL3(value) HW_CPU_GET_SYSREG(mair_el3, value) + #define HW_CPU_SET_MAIR_EL3(value) HW_CPU_SET_SYSREG(mair_el3, value) + + #define HW_CPU_GET_VBAR_EL3(value) HW_CPU_GET_SYSREG(vbar_el3, value) + #define HW_CPU_SET_VBAR_EL3(value) HW_CPU_SET_SYSREG(vbar_el3, value) + + #define HW_CPU_GET_CLIDR_EL1(value) HW_CPU_GET_SYSREG(clidr_el1, value) + #define HW_CPU_SET_CLIDR_EL1(value) HW_CPU_SET_SYSREG(clidr_el1, value) + + #define HW_CPU_GET_CCSIDR_EL1(value) HW_CPU_GET_SYSREG(ccsidr_el1, value) + #define HW_CPU_SET_CCSIDR_EL1(value) HW_CPU_SET_SYSREG(ccsidr_el1, value) + + #define HW_CPU_GET_CSSELR_EL1(value) HW_CPU_GET_SYSREG(csselr_el1, value) + #define HW_CPU_SET_CSSELR_EL1(value) HW_CPU_SET_SYSREG(csselr_el1, value) + + #define HW_CPU_GET_CPUACTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_0, value) + #define HW_CPU_SET_CPUACTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_0, value) + + #define HW_CPU_GET_CPUECTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_1, value) + #define HW_CPU_SET_CPUECTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_1, value) + + #define HW_CPU_GET_DBGAUTHSTATUS_EL1(value) HW_CPU_GET_SYSREG(dbgauthstatus_el1, value) + #define HW_CPU_SET_DBGAUTHSTATUS_EL1(value) HW_CPU_SET_SYSREG(dbgauthstatus_el1, value) + + #define HW_CPU_GET_MPIDR_EL1(value) HW_CPU_GET_SYSREG(mpidr_el1, value) + + #define HW_CPU_GET_OSLAR_EL1(value) HW_CPU_GET_SYSREG(oslar_el1, value) + #define HW_CPU_SET_OSLAR_EL1(value) HW_CPU_SET_SYSREG(oslar_el1, value) + + #define HW_CPU_GET_OSDTRRX_EL1(value) HW_CPU_GET_SYSREG(osdtrrx_el1, value) + #define HW_CPU_SET_OSDTRRX_EL1(value) HW_CPU_SET_SYSREG(osdtrrx_el1, value) + + #define HW_CPU_GET_OSDTRTX_EL1(value) HW_CPU_GET_SYSREG(osdtrtx_el1, value) + #define HW_CPU_SET_OSDTRTX_EL1(value) HW_CPU_SET_SYSREG(osdtrtx_el1, value) + + #define HW_CPU_GET_MDSCR_EL1(value) HW_CPU_GET_SYSREG(mdscr_el1, value) + #define HW_CPU_SET_MDSCR_EL1(value) HW_CPU_SET_SYSREG(mdscr_el1, value) + + #define HW_CPU_GET_OSECCR_EL1(value) HW_CPU_GET_SYSREG(oseccr_el1, value) + #define HW_CPU_SET_OSECCR_EL1(value) HW_CPU_SET_SYSREG(oseccr_el1, value) + + #define HW_CPU_GET_MDCCINT_EL1(value) HW_CPU_GET_SYSREG(mdccint_el1, value) + #define HW_CPU_SET_MDCCINT_EL1(value) HW_CPU_SET_SYSREG(mdccint_el1, value) + + #define HW_CPU_GET_DBGCLAIMCLR_EL1(value) HW_CPU_GET_SYSREG(dbgclaimclr_el1, value) + #define HW_CPU_SET_DBGCLAIMCLR_EL1(value) HW_CPU_SET_SYSREG(dbgclaimclr_el1, value) + + #define HW_CPU_GET_DBGVCR32_EL2(value) HW_CPU_GET_SYSREG(dbgvcr32_el2, value) + #define HW_CPU_SET_DBGVCR32_EL2(value) HW_CPU_SET_SYSREG(dbgvcr32_el2, value) + + #define HW_CPU_GET_SDER32_EL3(value) HW_CPU_GET_SYSREG(sder32_el3, value) + #define HW_CPU_SET_SDER32_EL3(value) HW_CPU_SET_SYSREG(sder32_el3, value) + + #define HW_CPU_GET_MDCR_EL2(value) HW_CPU_GET_SYSREG(mdcr_el2, value) + #define HW_CPU_SET_MDCR_EL2(value) HW_CPU_SET_SYSREG(mdcr_el2, value) + + #define HW_CPU_GET_MDCR_EL3(value) HW_CPU_GET_SYSREG(mdcr_el3, value) + #define HW_CPU_SET_MDCR_EL3(value) HW_CPU_SET_SYSREG(mdcr_el3, value) + + #define HW_CPU_GET_SPSR_EL3(value) HW_CPU_GET_SYSREG(spsr_el3, value) + #define HW_CPU_SET_SPSR_EL3(value) HW_CPU_SET_SYSREG(spsr_el3, value) + + #define HW_CPU_GET_DBGBVR0_EL1(value) HW_CPU_GET_SYSREG(dbgbvr0_el1, value) + #define HW_CPU_SET_DBGBVR0_EL1(value) HW_CPU_SET_SYSREG(dbgbvr0_el1, value) + #define HW_CPU_GET_DBGBCR0_EL1(value) HW_CPU_GET_SYSREG(dbgbcr0_el1, value) + #define HW_CPU_SET_DBGBCR0_EL1(value) HW_CPU_SET_SYSREG(dbgbcr0_el1, value) + #define HW_CPU_GET_DBGBVR1_EL1(value) HW_CPU_GET_SYSREG(dbgbvr1_el1, value) + #define HW_CPU_SET_DBGBVR1_EL1(value) HW_CPU_SET_SYSREG(dbgbvr1_el1, value) + #define HW_CPU_GET_DBGBCR1_EL1(value) HW_CPU_GET_SYSREG(dbgbcr1_el1, value) + #define HW_CPU_SET_DBGBCR1_EL1(value) HW_CPU_SET_SYSREG(dbgbcr1_el1, value) + #define HW_CPU_GET_DBGBVR2_EL1(value) HW_CPU_GET_SYSREG(dbgbvr2_el1, value) + #define HW_CPU_SET_DBGBVR2_EL1(value) HW_CPU_SET_SYSREG(dbgbvr2_el1, value) + #define HW_CPU_GET_DBGBCR2_EL1(value) HW_CPU_GET_SYSREG(dbgbcr2_el1, value) + #define HW_CPU_SET_DBGBCR2_EL1(value) HW_CPU_SET_SYSREG(dbgbcr2_el1, value) + #define HW_CPU_GET_DBGBVR3_EL1(value) HW_CPU_GET_SYSREG(dbgbvr3_el1, value) + #define HW_CPU_SET_DBGBVR3_EL1(value) HW_CPU_SET_SYSREG(dbgbvr3_el1, value) + #define HW_CPU_GET_DBGBCR3_EL1(value) HW_CPU_GET_SYSREG(dbgbcr3_el1, value) + #define HW_CPU_SET_DBGBCR3_EL1(value) HW_CPU_SET_SYSREG(dbgbcr3_el1, value) + #define HW_CPU_GET_DBGBVR4_EL1(value) HW_CPU_GET_SYSREG(dbgbvr4_el1, value) + #define HW_CPU_SET_DBGBVR4_EL1(value) HW_CPU_SET_SYSREG(dbgbvr4_el1, value) + #define HW_CPU_GET_DBGBCR4_EL1(value) HW_CPU_GET_SYSREG(dbgbcr4_el1, value) + #define HW_CPU_SET_DBGBCR4_EL1(value) HW_CPU_SET_SYSREG(dbgbcr4_el1, value) + #define HW_CPU_GET_DBGBVR5_EL1(value) HW_CPU_GET_SYSREG(dbgbvr5_el1, value) + #define HW_CPU_SET_DBGBVR5_EL1(value) HW_CPU_SET_SYSREG(dbgbvr5_el1, value) + #define HW_CPU_GET_DBGBCR5_EL1(value) HW_CPU_GET_SYSREG(dbgbcr5_el1, value) + #define HW_CPU_SET_DBGBCR5_EL1(value) HW_CPU_SET_SYSREG(dbgbcr5_el1, value) + + #define HW_CPU_GET_DBGWVR0_EL1(value) HW_CPU_GET_SYSREG(dbgwvr0_el1, value) + #define HW_CPU_SET_DBGWVR0_EL1(value) HW_CPU_SET_SYSREG(dbgwvr0_el1, value) + #define HW_CPU_GET_DBGWCR0_EL1(value) HW_CPU_GET_SYSREG(dbgwcr0_el1, value) + #define HW_CPU_SET_DBGWCR0_EL1(value) HW_CPU_SET_SYSREG(dbgwcr0_el1, value) + #define HW_CPU_GET_DBGWVR1_EL1(value) HW_CPU_GET_SYSREG(dbgwvr1_el1, value) + #define HW_CPU_SET_DBGWVR1_EL1(value) HW_CPU_SET_SYSREG(dbgwvr1_el1, value) + #define HW_CPU_GET_DBGWCR1_EL1(value) HW_CPU_GET_SYSREG(dbgwcr1_el1, value) + #define HW_CPU_SET_DBGWCR1_EL1(value) HW_CPU_SET_SYSREG(dbgwcr1_el1, value) + #define HW_CPU_GET_DBGWVR2_EL1(value) HW_CPU_GET_SYSREG(dbgwvr2_el1, value) + #define HW_CPU_SET_DBGWVR2_EL1(value) HW_CPU_SET_SYSREG(dbgwvr2_el1, value) + #define HW_CPU_GET_DBGWCR2_EL1(value) HW_CPU_GET_SYSREG(dbgwcr2_el1, value) + #define HW_CPU_SET_DBGWCR2_EL1(value) HW_CPU_SET_SYSREG(dbgwcr2_el1, value) + #define HW_CPU_GET_DBGWVR3_EL1(value) HW_CPU_GET_SYSREG(dbgwvr3_el1, value) + #define HW_CPU_SET_DBGWVR3_EL1(value) HW_CPU_SET_SYSREG(dbgwvr3_el1, value) + #define HW_CPU_GET_DBGWCR3_EL1(value) HW_CPU_GET_SYSREG(dbgwcr3_el1, value) + #define HW_CPU_SET_DBGWCR3_EL1(value) HW_CPU_SET_SYSREG(dbgwcr3_el1, value) + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/system-control-register-el3 */ + struct SctlrEl3 { + using M = util::BitPack32::Field< 0, 1>; + using A = util::BitPack32::Field< 1, 1>; + using C = util::BitPack32::Field< 2, 1>; + using Sa = util::BitPack32::Field< 3, 1>; + using I = util::BitPack32::Field<12, 1>; + using Wxn = util::BitPack32::Field<19, 1>; + using Ee = util::BitPack32::Field<25, 1>; + + static constexpr u32 Res1 = 0x30C50830; + }; + + /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABGIHHJ.html */ + struct ScrEl3 { + using Ns = util::BitPack32::Field< 0, 1>; + using Irq = util::BitPack32::Field< 1, 1>; + using Fiq = util::BitPack32::Field< 2, 1>; + using Ea = util::BitPack32::Field< 3, 1>; + using Fw = util::BitPack32::Field< 4, 1>; + using Aw = util::BitPack32::Field< 5, 1>; + using Net = util::BitPack32::Field< 6, 1>; + using Smd = util::BitPack32::Field< 7, 1>; + using Hce = util::BitPack32::Field< 8, 1>; + using Sif = util::BitPack32::Field< 9, 1>; + using RwCortexA53 = util::BitPack32::Field<10, 1>; + using StCortexA53 = util::BitPack32::Field<11, 1>; + using Twi = util::BitPack32::Field<12, 1>; + using Twe = util::BitPack32::Field<13, 1>; + }; + + /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/CIHDEBIG.html */ + struct CptrEl3 { + using Tfp = util::BitPack32::Field<10, 1>; + using Tta = util::BitPack32::Field<20, 1>; + using Tcpac = util::BitPack32::Field<31, 1>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/translation-control-register-el3 */ + struct TcrEl3 { + using T0sz = util::BitPack32::Field< 0, 6>; + using Irgn0 = util::BitPack32::Field< 8, 2>; + using Orgn0 = util::BitPack32::Field<10, 2>; + using Sh0 = util::BitPack32::Field<12, 2>; + using Tg0 = util::BitPack32::Field<14, 2>; + using Ps = util::BitPack32::Field<16, 3>; + using Tbi = util::BitPack32::Field<20, 1>; + + static constexpr u32 Res1 = 0x80800000; + }; + + struct ClidrEl1 { + using Ctype1 = util::BitPack32::Field< 0, 3>; + using Ctype2 = util::BitPack32::Field< 3, 3>; + using Ctype3 = util::BitPack32::Field< 6, 3>; + using Louis = util::BitPack32::Field<21, 3>; + using Loc = util::BitPack32::Field<24, 3>; + using Louu = util::BitPack32::Field<27, 3>; + }; + + struct CcsidrEl1 { + using LineSize = util::BitPack32::Field< 0, 3>; + using Associativity = util::BitPack32::Field< 3, 10>; + using NumSets = util::BitPack32::Field<13, 15>; + using Wa = util::BitPack32::Field<28, 1>; + using Ra = util::BitPack32::Field<29, 1>; + using Wb = util::BitPack32::Field<30, 1>; + using Wt = util::BitPack32::Field<31, 1>; + }; + + struct CsselrEl1 { + using InD = util::BitPack32::Field<0, 1>; + using Level = util::BitPack32::Field<1, 3>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-auxiliary-control-register-el1 */ + struct CpuactlrEl1CortexA57 { + /* TODO: Other bits */ + using NonCacheableStreamingEnhancement = util::BitPack64::Field<24, 1>; + /* TODO: Other bits */ + using DisableLoadPassDmb = util::BitPack64::Field<59, 1>; + using ForceProcessorRcgEnablesActive = util::BitPack64::Field<63, 1>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-extended-control-register-el1 */ + struct CpuectlrEl1CortexA57 { + using ProcessorDynamicRetentionControl = util::BitPack64::Field< 0, 2>; + using Smpen = util::BitPack64::Field< 6, 1>; + using L2LoadStoreDataPrefetchDistance = util::BitPack64::Field<32, 2>; + using L2InstructionFetchPrefetchDistance = util::BitPack64::Field<35, 2>; + using DisableTableWalkDescriptorAccessPrefetch = util::BitPack64::Field<38, 1>; + }; + + /* https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/dbgauthstatus_el1 */ + struct DbgAuthStatusEl1 { + using Nsid = util::BitPack32::Field<0, 2>; + using Nsnid = util::BitPack32::Field<2, 2>; + using Sid = util::BitPack32::Field<4, 2>; + using Snid = util::BitPack32::Field<6, 2>; + }; + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/i2c.hpp b/libraries/libexosphere/include/exosphere/i2c.hpp new file mode 100644 index 000000000..fd3b10d2b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/i2c.hpp @@ -0,0 +1,46 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::i2c { + + enum Port { + Port_1 = 0, + /* TODO: Support other ports? */ + Port_5 = 4, + + Port_Count = 5, + }; + + void SetRegisterAddress(Port port, uintptr_t address); + + void Initialize(Port port); + + bool Query(void *dst, size_t dst_size, Port port, int address, int r); + bool Send(Port port, int address, int r, const void *src, size_t src_size); + + inline u8 QueryByte(Port port, int address, int r) { + u8 byte; + Query(std::addressof(byte), sizeof(byte), port, address, r); + return byte; + } + + inline void SendByte(Port port, int address, int r, u8 byte) { + Send(port, address, r, std::addressof(byte), sizeof(byte)); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp new file mode 100644 index 000000000..2f1cca8c5 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/log.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::log { + + void Initialize(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/mmu.hpp b/libraries/libexosphere/include/exosphere/mmu.hpp new file mode 100644 index 000000000..c3b888e1d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu.hpp @@ -0,0 +1,18 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/mmu/mmu_api.hpp> \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp new file mode 100644 index 000000000..bedce5af7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp @@ -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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::mmu::arch::arm { + + /* NOTE: mmu is not supported by libexosphere-on-arm. */ + /* However, we want the memory layout to be parseable, so we will include some arm64 mmu definitions. */ + constexpr inline u64 L1EntryShift = 30; + constexpr inline u64 L2EntryShift = 21; + constexpr inline u64 L3EntryShift = 12; + + constexpr inline u64 L1EntrySize = 1_GB; + constexpr inline u64 L2EntrySize = 2_MB; + constexpr inline u64 L3EntrySize = 4_KB; + + constexpr inline u64 PageSize = L3EntrySize; + + constexpr inline u64 L1EntryMask = ((static_cast<u64>(1) << (48 - L1EntryShift)) - 1) << L1EntryShift; + constexpr inline u64 L2EntryMask = ((static_cast<u64>(1) << (48 - L2EntryShift)) - 1) << L2EntryShift; + constexpr inline u64 L3EntryMask = ((static_cast<u64>(1) << (48 - L3EntryShift)) - 1) << L3EntryShift; + + constexpr inline u64 TableEntryMask = L3EntryMask; + + static_assert(L1EntryMask == 0x0000FFFFC0000000ul); + static_assert(L2EntryMask == 0x0000FFFFFFE00000ul); + static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul); + +} diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp new file mode 100644 index 000000000..d18e2b4f1 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp @@ -0,0 +1,279 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::mmu::arch::arm64 { + + enum PageTableTableAttribute : u64 { + PageTableTableAttribute_None = (0ul << 0), + PageTableTableAttribute_PrivilegedExecuteNever = (1ul << 59), + PageTableTableAttribute_ExecuteNever = (1ul << 60), + PageTableTableAttribute_NonSecure = (1ul << 63), + + PageTableTableAttributes_El3SecureCode = PageTableTableAttribute_None, + PageTableTableAttributes_El3SecureData = PageTableTableAttribute_ExecuteNever, + PageTableTableAttributes_El3NonSecureCode = PageTableTableAttributes_El3SecureCode | PageTableTableAttribute_NonSecure, + PageTableTableAttributes_El3NonSecureData = PageTableTableAttributes_El3SecureData | PageTableTableAttribute_NonSecure, + }; + + enum PageTableMappingAttribute : u64{ + /* Security. */ + PageTableMappingAttribute_NonSecure = (1ul << 5), + + /* El1 Access. */ + PageTableMappingAttribute_El1NotAllowed = (0ul << 6), + PageTableMappingAttribute_El1Allowed = (1ul << 6), + + /* RW Permission. */ + PageTableMappingAttribute_PermissionReadWrite = (0ul << 7), + PageTableMappingAttribute_PermissionReadOnly = (1ul << 7), + + /* Shareability. */ + PageTableMappingAttribute_ShareabilityNonShareable = (0ul << 8), + PageTableMappingAttribute_ShareabiltiyOuterShareable = (2ul << 8), + PageTableMappingAttribute_ShareabilityInnerShareable = (3ul << 8), + + /* Access flag. */ + PageTableMappingAttribute_AccessFlagNotAccessed = (0ul << 10), + PageTableMappingAttribute_AccessFlagAccessed = (1ul << 10), + + /* Global. */ + PageTableMappingAttribute_Global = (0ul << 11), + PageTableMappingAttribute_NonGlobal = (1ul << 11), + + /* Contiguous */ + PageTableMappingAttribute_NonContiguous = (0ul << 52), + PageTableMappingAttribute_Contiguous = (1ul << 52), + + /* Privileged Execute Never */ + PageTableMappingAttribute_PrivilegedExecuteNever = (1ul << 53), + + /* Execute Never */ + PageTableMappingAttribute_ExecuteNever = (1ul << 54), + + + /* Useful definitions. */ + PageTableMappingAttributes_El3SecureRwCode = ( + PageTableMappingAttribute_PermissionReadWrite | + PageTableMappingAttribute_ShareabilityInnerShareable + ), + + PageTableMappingAttributes_El3SecureRoCode = ( + PageTableMappingAttribute_PermissionReadOnly | + PageTableMappingAttribute_ShareabilityInnerShareable + ), + + PageTableMappingAttributes_El3SecureRoData = ( + PageTableMappingAttribute_PermissionReadOnly | + PageTableMappingAttribute_ShareabilityInnerShareable | + PageTableMappingAttribute_ExecuteNever + ), + + PageTableMappingAttributes_El3SecureRwData = ( + PageTableMappingAttribute_PermissionReadWrite | + PageTableMappingAttribute_ShareabilityInnerShareable | + PageTableMappingAttribute_ExecuteNever + ), + + PageTableMappingAttributes_El3NonSecureRwCode = PageTableMappingAttributes_El3SecureRwCode | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRoCode = PageTableMappingAttributes_El3SecureRoCode | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRoData = PageTableMappingAttributes_El3SecureRoData | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRwData = PageTableMappingAttributes_El3SecureRwData | PageTableMappingAttribute_NonSecure, + + + PageTableMappingAttributes_El3SecureDevice = PageTableMappingAttributes_El3SecureRwData, + PageTableMappingAttributes_El3NonSecureDevice = PageTableMappingAttributes_El3NonSecureRwData, + }; + + enum MemoryRegionAttribute : u64 { + MemoryRegionAttribute_Device_nGnRnE = (0ul << 2), + MemoryRegionAttribute_Device_nGnRE = (1ul << 2), + MemoryRegionAttribute_NormalMemory = (2ul << 2), + MemoryRegionAttribute_NormalMemoryNotCacheable = (3ul << 2), + + MemoryRegionAttribute_NormalInnerShift = 0, + MemoryRegionAttribute_NormalOuterShift = 4, + + #define AMS_MRA_DEFINE_NORMAL_ATTR(__NAME__, __VAL__) \ + MemoryRegionAttribute_NormalInner##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalInnerShift), \ + MemoryRegionAttribute_NormalOuter##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalOuterShift) + + AMS_MRA_DEFINE_NORMAL_ATTR(NonCacheable, 4), + + AMS_MRA_DEFINE_NORMAL_ATTR(WriteAllocate, (1ul << 0)), + AMS_MRA_DEFINE_NORMAL_ATTR(ReadAllocate, (1ul << 1)), + + AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughTransient, (0ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackTransient, (1ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughNonTransient, (2ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackNonTransient, (3ul << 2)), + + #undef AMS_MRA_DEFINE_NORMAL_ATTR + + MemoryRegionAttributes_Normal = ( + MemoryRegionAttribute_NormalInnerReadAllocate | + MemoryRegionAttribute_NormalOuterReadAllocate | + MemoryRegionAttribute_NormalInnerWriteAllocate | + MemoryRegionAttribute_NormalOuterWriteAllocate | + MemoryRegionAttribute_NormalInnerWriteBackNonTransient | + MemoryRegionAttribute_NormalOuterWriteBackNonTransient + ), + + MemoryRegionAttributes_Device = ( + MemoryRegionAttribute_Device_nGnRE + ), + }; + + constexpr inline u64 MemoryRegionAttributeWidth = 8; + + constexpr PageTableMappingAttribute AddMappingAttributeIndex(PageTableMappingAttribute attr, int index) { + return static_cast<PageTableMappingAttribute>(attr | (static_cast<typename std::underlying_type<PageTableMappingAttribute>::type>(index) << 2)); + } + + constexpr inline u64 L1EntryShift = 30; + constexpr inline u64 L2EntryShift = 21; + constexpr inline u64 L3EntryShift = 12; + + constexpr inline u64 L1EntrySize = 1_GB; + constexpr inline u64 L2EntrySize = 2_MB; + constexpr inline u64 L3EntrySize = 4_KB; + + constexpr inline u64 PageSize = L3EntrySize; + + constexpr inline u64 L1EntryMask = ((1ul << (48 - L1EntryShift)) - 1) << L1EntryShift; + constexpr inline u64 L2EntryMask = ((1ul << (48 - L2EntryShift)) - 1) << L2EntryShift; + constexpr inline u64 L3EntryMask = ((1ul << (48 - L3EntryShift)) - 1) << L3EntryShift; + + constexpr inline u64 TableEntryMask = L3EntryMask; + + static_assert(L1EntryMask == 0x0000FFFFC0000000ul); + static_assert(L2EntryMask == 0x0000FFFFFFE00000ul); + static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul); + + constexpr inline u64 TableEntryIndexMask = 0x1FF; + + constexpr inline u64 EntryBlock = 0x1ul; + constexpr inline u64 EntryPage = 0x3ul; + + constexpr u64 MakeTableEntry(u64 address, PageTableTableAttribute attr) { + return address | static_cast<u64>(attr) | 0x3ul; + } + + constexpr u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast<u64>(attr) | 0x1ul; + } + + constexpr u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast<u64>(attr) | 0x1ul; + } + + constexpr u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast<u64>(attr) | 0x3ul; + } + + constexpr uintptr_t GetL2Offset(uintptr_t address) { + return address & ((1ul << L2EntryShift) - 1); + } + + constexpr u64 GetL1EntryIndex(uintptr_t address) { + return ((address >> L1EntryShift) & TableEntryIndexMask); + } + + constexpr u64 GetL2EntryIndex(uintptr_t address) { + return ((address >> L2EntryShift) & TableEntryIndexMask); + } + + constexpr u64 GetL3EntryIndex(uintptr_t address) { + return ((address >> L3EntryShift) & TableEntryIndexMask); + } + + constexpr ALWAYS_INLINE void SetTableImpl(u64 *table, u64 index, u64 value) { + /* Ensure (for constexpr validation purposes) that the entry we set is clear. */ + if (table[index]) { + __builtin_unreachable(); + } + + /* Set the value. */ + table[index] = value; + } + + constexpr void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + SetTableImpl(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + } + + constexpr void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + SetTableImpl(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + } + + constexpr void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL1EntryIndex(virt_addr); + const u64 count = (size >> L1EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL1BlockEntry((phys_addr & L1EntryMask) + (i << L1EntryShift), attr)); + } + } + + constexpr void SetL2BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL2EntryIndex(virt_addr); + const u64 count = (size >> L2EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL2BlockEntry((phys_addr & L2EntryMask) + (i << L2EntryShift), attr)); + } + } + + constexpr void SetL3BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL3EntryIndex(virt_addr); + const u64 count = (size >> L3EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL3BlockEntry((phys_addr & L3EntryMask) + (i << L3EntryShift), attr)); + } + } + + constexpr void InvalidateL1Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL1EntryIndex(virt_addr); + const u64 count = (size >> L1EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + + constexpr void InvalidateL2Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL2EntryIndex(virt_addr); + const u64 count = (size >> L2EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + + constexpr void InvalidateL3Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL3EntryIndex(virt_addr); + const u64 count = (size >> L3EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + +} diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp new file mode 100644 index 000000000..1c0e02801 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp @@ -0,0 +1,37 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include <exosphere/mmu/mmu_api.arch.arm64.hpp> +#elif defined(ATMOSPHERE_ARCH_ARM) + #include <exosphere/mmu/mmu_api.arch.arm.hpp> +#else + #error "Unknown architecture for mmu!" +#endif + +namespace ams::mmu { + + #if defined(ATMOSPHERE_ARCH_ARM64) + using namespace ams::mmu::arch::arm64; + #elif defined(ATMOSPHERE_ARCH_ARM) + using namespace ams::mmu::arch::arm; + #else + #error "Unknown architecture for mmu!" + #endif + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg1.hpp b/libraries/libexosphere/include/exosphere/pkg1.hpp new file mode 100644 index 000000000..d7dbad6af --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1.hpp @@ -0,0 +1,22 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/pkg1/pkg1_bootloader_parameters.hpp> +#include <exosphere/pkg1/pkg1_boot_config.hpp> +#include <exosphere/pkg1/pkg1_error_types.hpp> +#include <exosphere/pkg1/pkg1_key_generation.hpp> +#include <exosphere/pkg1/pkg1_se_key_slots.hpp> diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp new file mode 100644 index 000000000..da73f0112 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -0,0 +1,133 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + enum MemorySize { + MemorySize_4GB = 0, + MemorySize_6GB = 1, + MemorySize_8GB = 2, + }; + + enum MemoryArrange { + MemoryArrange_Normal = 1, + MemoryArrange_AppletDev = 2, + MemoryArrange_SystemDev = 2, + }; + + enum MemoryMode { + MemoryMode_SizeShift = 4, + MemoryMode_SizeMask = 0x30, + + MemoryMode_ArrangeMask = 0x0F, + + MemoryMode_Auto = 0x00, + + MemoryMode_4GB = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + MemoryMode_4GBAppletDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)), + MemoryMode_4GBSystemDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_SystemDev)), + + MemoryMode_6GB = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + MemoryMode_6GBAppletDev = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)), + + MemoryMode_8GB = ((MemorySize_8GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + }; + + constexpr ALWAYS_INLINE MemorySize GetMemorySize(MemoryMode mode) { + return static_cast<MemorySize>(mode >> MemoryMode_SizeShift); + } + + constexpr ALWAYS_INLINE MemoryArrange GetMemoryArrange(MemoryMode mode) { + return static_cast<MemoryArrange>(mode & MemoryMode_ArrangeMask); + } + + constexpr ALWAYS_INLINE MemoryMode MakeMemoryMode(MemorySize size, MemoryArrange arrange) { + return static_cast<MemoryMode>((size << MemoryMode_SizeShift) | (arrange)); + } + + struct BootConfigData { + u32 version; + u32 reserved_04; + u32 reserved_08; + u32 reserved_0C; + u8 flags1[0x10]; + u8 flags0[0x10]; + u64 initial_tsc_value; + u8 padding_38[0x200 - 0x38]; + + constexpr bool IsDevelopmentFunctionEnabled() const { + return (this->flags1[0] & (1 << 1)) != 0; + } + + constexpr bool IsSErrorDebugEnabled() const { + return (this->flags1[0] & (1 << 2)) != 0; + } + + constexpr u8 GetKernelFlags0() const { + return this->flags0[1]; + } + + constexpr u8 GetKernelFlags1() const { + return this->flags1[0]; + } + + constexpr MemoryMode GetMemoryMode() const { + return static_cast<MemoryMode>(this->flags0[3]); + } + + bool IsTscInitialValueValid() const { + return (this->flags0[4] & (1 << 0)) != 0; + } + }; + static_assert(util::is_pod<BootConfigData>::value); + static_assert(sizeof(BootConfigData) == 0x200); + + struct BootConfigSignedData { + u32 version; + u32 reserved_04; + u8 flags; + u8 reserved_09[0x10 - 9]; + u8 ecid[0x10]; + u8 flags1[0x10]; + u8 flags0[0x10]; + u8 padding_40[0x100 - 0x40]; + + constexpr bool IsPackage2EncryptionDisabled() const { + return (this->flags & (1 << 0)) != 0; + } + + constexpr bool IsPackage2SignatureVerificationDisabled() const { + return (this->flags & (1 << 1)) != 0; + } + + constexpr bool IsProgramVerificationDisabled() const { + return (this->flags1[0] & (1 << 0)) != 0; + } + }; + static_assert(util::is_pod<BootConfigSignedData>::value); + static_assert(sizeof(BootConfigSignedData) == 0x100); + + struct BootConfig { + BootConfigData data; + u8 signature[0x100]; + BootConfigSignedData signed_data; + }; + static_assert(util::is_pod<BootConfig>::value); + static_assert(sizeof(BootConfig) == 0x400); + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp new file mode 100644 index 000000000..93f70df18 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp @@ -0,0 +1,68 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + enum BootloaderState { + BootloaderState_Start = 0, + BootloaderState_LoadedBootConfig = 1, + BootloaderState_InitializedDram = 2, + BootloaderState_LoadedPackage2 = 3, + BootloaderState_Done = 4, + }; + + enum SecureMonitorState { + SecureMonitorState_Start = 0, + SecureMonitorState_Initialized = 1, + }; + + struct BctParameters { + u32 bootloader_version; + u32 bootloader_start_block; + u32 bootloader_start_page; + u32 bootloader_attributes; + }; + static_assert(util::is_pod<BctParameters>::value && sizeof(BctParameters) == 0x10); + + struct SecureMonitorParameters { + u32 bootloader_start_time; + u32 bootloader_end_time; + u32 secmon_start_time; + u32 secmon_end_time; + BctParameters bct_params; + u8 reserved[0xD8]; + u32 bootloader_state; + u32 secmon_state; + u8 reserved2[0x100]; + }; + static_assert(util::is_pod<SecureMonitorParameters>::value); + static_assert(sizeof(SecureMonitorParameters) == 0x200); + + static_assert(offsetof(SecureMonitorParameters, bct_params) == 0x10); + static_assert(offsetof(SecureMonitorParameters, bootloader_state) == 0xF8); + static_assert(offsetof(SecureMonitorParameters, secmon_state) == 0xFC); + + enum BootloaderAttribute { + BootloaderAttribute_None = (0u << 0), + BootloaderAttribute_RecoveryBoot = (1u << 0), + + BootloaderAttribute_RestrictedSmcShift = 1, + BootloaderAttribute_RestrictedSmcMask = (0xFu << BootloaderAttribute_RestrictedSmcShift), + }; + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp new file mode 100644 index 000000000..76331434d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp @@ -0,0 +1,108 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + enum ErrorReason { + ErrorReason_None = 0, + ErrorReason_InvalidPackage2Signature = 1, + ErrorReason_InvalidPackage2Meta = 2, + ErrorReason_InvalidPackage2Version = 3, + ErrorReason_InvalidPackage2Payload = 4, + ErrorReason_UnknownSmc = 5, + ErrorReason_UnknownAbort = 6, + ErrorReason_InvalidCoreContext = 7, + ErrorReason_InvalidSecurityEngineStickyBits = 8, + ErrorReason_UnexpectedReset = 9, + + ErrorReason_Exception = 0x10, + + ErrorReason_TransitionToSafeMode = 0x20, + ErrorReason_SecureInitializerReboot = 0x21, + + ErrorReason_SdmmcError = 0x30, + ErrorReason_InvalidDramId = 0x31, + ErrorReason_InvalidPackage2 = 0x32, + ErrorReason_InvalidBct = 0x33, + ErrorReason_InvalidGpt = 0x34, + ErrorReason_FailedToTransitionToSafeMode = 0x35, + ErrorReason_ActivityMonitorInterrupt = 0x36, + + ErrorReason_KernelPanic = 0x40, + }; + + enum ErrorColor { + ErrorColor_Black = 0x000, + + ErrorColor_Red = 0x00F, + ErrorColor_Yellow = 0x0FF, + ErrorColor_Orange = 0x07F, + ErrorColor_Blue = 0xF00, + ErrorColor_LightBlue = 0xFF0, + ErrorColor_Pink = 0xF7F, + ErrorColor_Purple = 0xF0A, + }; + + enum ErrorInfo { + ErrorInfo_ReasonMask = 0xFF, + ErrorInfo_ColorShift = 20, + + #define MAKE_ERROR_INFO(_COLOR_, _DESC_) ((static_cast<u32>(ErrorColor_##_COLOR_) << ErrorInfo_ColorShift) | (ErrorReason_##_DESC_)) + + ErrorInfo_None = MAKE_ERROR_INFO(Black, None), + + ErrorInfo_InvalidPackage2Signature = MAKE_ERROR_INFO(Blue, InvalidPackage2Signature), + ErrorInfo_InvalidPackage2Meta = MAKE_ERROR_INFO(Blue, InvalidPackage2Meta), + ErrorInfo_InvalidPackage2Version = MAKE_ERROR_INFO(Blue, InvalidPackage2Version), + ErrorInfo_InvalidPackage2Payload = MAKE_ERROR_INFO(Blue, InvalidPackage2Payload), + + ErrorInfo_UnknownSmc = MAKE_ERROR_INFO(LightBlue, UnknownSmc), + + ErrorInfo_UnknownAbort = MAKE_ERROR_INFO(Yellow, UnknownAbort), + + ErrorInfo_InvalidCoreContext = MAKE_ERROR_INFO(Pink, InvalidCoreContext), + ErrorInfo_InvalidSecurityEngineStickyBits = MAKE_ERROR_INFO(Pink, InvalidSecurityEngineStickyBits), + ErrorInfo_UnexpectedReset = MAKE_ERROR_INFO(Pink, UnexpectedReset), + + ErrorInfo_Exception = MAKE_ERROR_INFO(Orange, Exception), + + ErrorInfo_TransitionToSafeMode = MAKE_ERROR_INFO(Black, TransitionToSafeMode), + ErrorInfo_SecureInitializerReboot = MAKE_ERROR_INFO(Black, SecureInitializerReboot), + + ErrorInfo_SdmmcError = MAKE_ERROR_INFO(Purple, SdmmcError), + ErrorInfo_InvalidDramId = MAKE_ERROR_INFO(Purple, InvalidDramId), + ErrorInfo_InvalidPackage2 = MAKE_ERROR_INFO(Purple, InvalidPackage2), + ErrorInfo_InvalidBct = MAKE_ERROR_INFO(Purple, InvalidBct), + ErrorInfo_InvalidGpt = MAKE_ERROR_INFO(Purple, InvalidGpt), + ErrorInfo_FailedToTransitionToSafeMode = MAKE_ERROR_INFO(Purple, FailedToTransitionToSafeMode), + ErrorInfo_ActivityMonitorInterrupt = MAKE_ERROR_INFO(Purple, ActivityMonitorInterrupt), + + #undef MAKE_ERROR_INFO + }; + + constexpr inline ErrorReason GetErrorReason(u32 info) { + return static_cast<ErrorReason>(info & ErrorInfo_ReasonMask); + } + + constexpr inline ErrorInfo MakeKernelPanicResetInfo(u32 color) { + return static_cast<ErrorInfo>((color << ErrorInfo_ColorShift) | (ErrorReason_KernelPanic)); + } + + #define PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH (0x840) + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp new file mode 100644 index 000000000..711f36ba4 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp @@ -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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + enum KeyGeneration : int { + + KeyGeneration_1_0_0 = 0x00, + KeyGeneration_3_0_0 = 0x01, + KeyGeneration_3_0_1 = 0x02, + KeyGeneration_4_0_0 = 0x03, + KeyGeneration_5_0_0 = 0x04, + KeyGeneration_6_0_0 = 0x05, + KeyGeneration_6_2_0 = 0x06, + KeyGeneration_7_0_0 = 0x07, + KeyGeneration_8_1_0 = 0x08, + KeyGeneration_9_0_0 = 0x09, + KeyGeneration_9_1_0 = 0x0A, + + KeyGeneration_Count, + + KeyGeneration_Current = KeyGeneration_Count - 1, + + KeyGeneration_Min = 0x00, + KeyGeneration_Max = 0x20, + }; + static_assert(KeyGeneration_Count <= KeyGeneration_Max); + + constexpr inline const int OldMasterKeyCount = KeyGeneration_Count - 1; + constexpr inline const int OldDeviceMasterKeyCount = KeyGeneration_Count - KeyGeneration_4_0_0; + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp new file mode 100644 index 000000000..614aa5a38 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -0,0 +1,56 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + enum AesKeySlot { + AesKeySlot_UserStart = 0, + + AesKeySlot_TzramSave = 2, + + AesKeySlot_UserLast = 5, + AesKeySlot_UserEnd = AesKeySlot_UserLast + 1, + + AesKeySlot_SecmonStart = 8, + + AesKeySlot_Temporary = 8, + AesKeySlot_Smc = 9, + AesKeySlot_RandomForUserWrap = 10, + AesKeySlot_RandomForKeyStorageWrap = 11, + AesKeySlot_DeviceMaster = 12, + AesKeySlot_Master = 13, + AesKeySlot_Device = 15, + + AesKeySlot_SecmonEnd = 16, + + /* Used only during boot. */ + AesKeySlot_Tsec = 12, + AesKeySlot_TsecRoot = 13, + AesKeySlot_SecureBoot = 14, + AesKeySlot_SecureStorage = 15, + + AesKeySlot_MasterKek = 13, + AesKeySlot_DeviceMasterKeySourceKek = 14, + }; + + enum RsaKeySlot { + RsaKeySlot_Temporary = 0, + RsaKeySlot_PrivateKey = 1, + }; + +} diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp new file mode 100644 index 000000000..d1e2091b4 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pmc.hpp @@ -0,0 +1,50 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pmc { + + enum SecureRegister { + SecureRegister_Other = (1 << 0), + SecureRegister_DramParameters = (1 << 1), + SecureRegister_ResetVector = (1 << 2), + SecureRegister_Carveout = (1 << 3), + SecureRegister_CmacWrite = (1 << 4), + SecureRegister_CmacRead = (1 << 5), + SecureRegister_KeySourceWrite = (1 << 6), + SecureRegister_KeySourceRead = (1 << 7), + SecureRegister_Srk = (1 << 8), + + SecureRegister_CmacReadWrite = SecureRegister_CmacRead | SecureRegister_CmacWrite, + SecureRegister_KeySourceReadWrite = SecureRegister_KeySourceRead | SecureRegister_KeySourceWrite, + }; + + void SetRegisterAddress(uintptr_t address); + + void InitializeRandomScratch(); + + void LockSecureRegister(SecureRegister reg); + + enum class LockState { + Locked = 0, + NotLocked = 1, + PartiallyLocked = 2, + }; + + LockState GetSecureRegisterLockState(SecureRegister reg); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp new file mode 100644 index 000000000..46b10372b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pmic.hpp @@ -0,0 +1,33 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pmic { + + enum Regulator { + /* Erista regulators. */ + Regulator_Erista_Max77621 = 0, /* Device code 0x3A000001 */ + + /* Mariko regulators. */ + Regulator_Mariko_Max77812_A = 1, /* Device code 0x3A000002 */ + Regulator_Mariko_Max77812_B = 2, /* Device code 0x3A000006 */ + }; + + void EnableVddCpu(Regulator regulator); + void DisableVddCpu(Regulator regulator); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp new file mode 100644 index 000000000..a16d66193 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/reg.hpp @@ -0,0 +1,205 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::reg { + + using BitsValue = std::tuple<u32, u32, u32>; + using BitsMask = std::tuple<u32, u32>; + + constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); } + constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); } + + constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) { + return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); + } + + constexpr ALWAYS_INLINE u32 EncodeMask(const BitsValue v) { + return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); + } + + constexpr ALWAYS_INLINE u32 EncodeValue(const BitsValue v) { + return ((GetValue(v) << GetOffset(v)) & EncodeMask(v)); + } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + constexpr ALWAYS_INLINE u32 Encode(const Values... values) { + return (EncodeValue(values) | ...); + } + + ALWAYS_INLINE void Write(volatile u32 *reg, u32 val) { *reg = val; } + ALWAYS_INLINE void Write(volatile u32 ®, u32 val) { reg = val; } + ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast<volatile u32 *>(reg), val); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void Write(volatile u32 *reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void Write(volatile u32 ®, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + ALWAYS_INLINE u32 Read(volatile u32 *reg) { return *reg; } + ALWAYS_INLINE u32 Read(volatile u32 ®) { return reg; } + ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast<volatile u32 *>(reg)); } + + ALWAYS_INLINE u32 Read(volatile u32 *reg, u32 mask) { return *reg & mask; } + ALWAYS_INLINE u32 Read(volatile u32 ®, u32 mask) { return reg & mask; } + ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast<volatile u32 *>(reg), mask); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE u32 Read(volatile u32 *reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE u32 Read(volatile u32 ®, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE bool HasValue(volatile u32 *reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE bool HasValue(volatile u32 ®, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + ALWAYS_INLINE void ReadWrite(volatile u32 *reg, u32 val, u32 mask) { *reg = (*reg & (~mask)) | (val & mask); } + ALWAYS_INLINE void ReadWrite(volatile u32 ®, u32 val, u32 mask) { reg = ( reg & (~mask)) | (val & mask); } + ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile u32 *reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile u32 ®, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) + ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + ALWAYS_INLINE void SetBits(volatile u32 *reg, u32 mask) { *reg = *reg | mask; } + ALWAYS_INLINE void SetBits(volatile u32 ®, u32 mask) { reg = reg | mask; } + ALWAYS_INLINE void SetBits(uintptr_t reg, u32 mask) { SetBits(reinterpret_cast<volatile u32 *>(reg), mask); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void SetBits(volatile u32 *reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void SetBits(volatile u32 ®, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + ALWAYS_INLINE void ClearBits(volatile u32 *reg, u32 mask) { *reg = *reg & ~mask; } + ALWAYS_INLINE void ClearBits(volatile u32 ®, u32 mask) { reg = reg & ~mask; } + ALWAYS_INLINE void ClearBits(uintptr_t reg, u32 mask) { ClearBits(reinterpret_cast<volatile u32 *>(reg), mask); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void ClearBits(volatile u32 *reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void ClearBits(volatile u32 ®, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + ALWAYS_INLINE void MaskBits(volatile u32 *reg, u32 mask) { *reg = *reg & mask; } + ALWAYS_INLINE void MaskBits(volatile u32 ®, u32 mask) { reg = reg & mask; } + ALWAYS_INLINE void MaskBits(uintptr_t reg, u32 mask) { MaskBits(reinterpret_cast<volatile u32 *>(reg), mask); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void MaskBits(volatile u32 *reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void MaskBits(volatile u32 ®, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...)) + ALWAYS_INLINE void MaskBits(uintptr_t reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + #define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH} + #define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE} + + #define REG_NAMED_BITS_MASK(PREFIX, NAME) REG_BITS_MASK(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH) + #define REG_NAMED_BITS_VALUE(PREFIX, NAME, VALUE) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, VALUE) + #define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM) + + #define REG_NAMED_BITS_ENUM_SEL(PREFIX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, (__COND__) ? PREFIX##_##NAME##_##TRUE_ENUM : PREFIX##_##NAME##_##FALSE_ENUM) + + #define REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, __WIDTH__) \ + constexpr inline u32 PREFIX##_##NAME##_OFFSET = __OFFSET__; \ + constexpr inline u32 PREFIX##_##NAME##_WIDTH = __WIDTH__ + + #define REG_DEFINE_NAMED_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 1); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + }; + + #define REG_DEFINE_NAMED_TWO_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 2); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + }; + + #define REG_DEFINE_NAMED_THREE_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 3); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + PREFIX##_##NAME##_##FOUR = 4, \ + PREFIX##_##NAME##_##FIVE = 5, \ + PREFIX##_##NAME##_##SIX = 6, \ + PREFIX##_##NAME##_##SEVEN = 7, \ + }; + + #define REG_DEFINE_NAMED_FOUR_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 4); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + PREFIX##_##NAME##_##FOUR = 4, \ + PREFIX##_##NAME##_##FIVE = 5, \ + PREFIX##_##NAME##_##SIX = 6, \ + PREFIX##_##NAME##_##SEVEN = 7, \ + PREFIX##_##NAME##_##EIGHT = 8, \ + PREFIX##_##NAME##_##NINE = 9, \ + PREFIX##_##NAME##_##TEN = 10, \ + PREFIX##_##NAME##_##ELEVEN = 11, \ + PREFIX##_##NAME##_##TWELVE = 12, \ + PREFIX##_##NAME##_##THIRTEEN = 13, \ + PREFIX##_##NAME##_##FOURTEEN = 14, \ + PREFIX##_##NAME##_##FIFTEEN = 15, \ + }; + +} diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp new file mode 100644 index 000000000..ad9abd632 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#include <exosphere/se/se_common.hpp> +#include <exosphere/se/se_management.hpp> +#include <exosphere/se/se_aes.hpp> +#include <exosphere/se/se_rsa.hpp> +#include <exosphere/se/se_rng.hpp> +#include <exosphere/se/se_suspend.hpp> diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp new file mode 100644 index 000000000..c2873f115 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -0,0 +1,36 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/se/se_common.hpp> + +namespace ams::se { + + constexpr inline int AesKeySlotCount = 16; + constexpr inline size_t AesBlockSize = crypto::AesEncryptor128::BlockSize; + + void ClearAesKeySlot(int slot); + void LockAesKeySlot(int slot, u32 flags); + + void SetAesKey(int slot, const void *key, size_t key_size); + + void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size); + void SetEncryptedAesKey256(int dst_slot, int kek_slot, const void *key, size_t key_size); + + void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/se/se_common.hpp b/libraries/libexosphere/include/exosphere/se/se_common.hpp new file mode 100644 index 000000000..4a87d642b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_common.hpp @@ -0,0 +1,53 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::secmon { + + u8 *GetSecurityEngineEphemeralWorkBlock(); + +} + +namespace ams::se { + + using DoneHandler = void(*)(); + + enum KeySlotLockFlags { + KeySlotLockFlags_None = 0, + KeySlotLockFlags_KeyRead = (1u << 0), + KeySlotLockFlags_KeyWrite = (1u << 1), + KeySlotLockFlags_OriginalIvRead = (1u << 2), + KeySlotLockFlags_OriginalIvWrite = (1u << 3), + KeySlotLockFlags_UpdatedIvRead = (1u << 4), + KeySlotLockFlags_UpdatedIvWrite = (1u << 5), + KeySlotLockFlags_KeyUse = (1u << 6), + KeySlotLockFlags_DstKeyTableOnly = (1u << 7), + KeySlotLockFlags_PerKey = (1u << 8), + + KeySlotLockFlags_AllReadLock = (KeySlotLockFlags_KeyRead | KeySlotLockFlags_OriginalIvRead | KeySlotLockFlags_UpdatedIvRead), + KeySlotLockFlags_AllLockKek = 0x1FF, + KeySlotLockFlags_AllLockKey = (KeySlotLockFlags_AllLockKek & ~KeySlotLockFlags_DstKeyTableOnly), + + KeySlotLockFlags_EristaMask = 0x7F, + KeySlotLockFlags_MarikoMask = 0xFF, + }; + + ALWAYS_INLINE u8 *GetEphemeralWorkBlock() { + return ::ams::secmon::GetSecurityEngineEphemeralWorkBlock(); + } + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp new file mode 100644 index 000000000..f5a8e6bea --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -0,0 +1,33 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::se { + + void SetRegisterAddress(uintptr_t address); + + void Initialize(); + + void SetSecure(bool secure); + void SetTzramSecure(); + void SetPerKeySecure(); + + void Lockout(); + + void HandleInterrupt(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/se/se_rng.hpp b/libraries/libexosphere/include/exosphere/se/se_rng.hpp new file mode 100644 index 000000000..3fa339363 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_rng.hpp @@ -0,0 +1,28 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::se { + + void InitializeRandom(); + + void GenerateRandomBytes(void *dst, size_t size); + void SetRandomKey(int slot); + + void GenerateSrk(); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp new file mode 100644 index 000000000..e16b99c66 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::se { + + constexpr inline int RsaKeySlotCount = 2; + constexpr inline int RsaSize = 0x100; + + void ClearRsaKeySlot(int slot); + void LockRsaKeySlot(int slot, u32 flags); + + void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp new file mode 100644 index 000000000..9f16924b7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp @@ -0,0 +1,55 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/se/se_aes.hpp> +#include <exosphere/se/se_rsa.hpp> + +namespace ams::se { + + /* 256-bit AES keyslots are two 128-bit keys. */ + constexpr inline int AesKeySlotPartCount = 2; + + /* RSA keys are both a modulus and an exponent. */ + constexpr inline int RsaKeySlotPartCount = 2; + + constexpr inline size_t StickyBitContextSize = 2 * AesBlockSize; + + struct Context { + u8 random[AesBlockSize]; + u8 sticky_bits[StickyBitContextSize / AesBlockSize][AesBlockSize]; + u8 aes_key[AesKeySlotCount][AesKeySlotPartCount][AesBlockSize]; + u8 aes_oiv[AesKeySlotCount][AesBlockSize]; + u8 aes_uiv[AesKeySlotCount][AesBlockSize]; + u8 rsa_key[RsaKeySlotCount][RsaKeySlotPartCount][RsaSize / AesBlockSize][AesBlockSize]; + u8 fixed_pattern[AesBlockSize]; + }; + static_assert(sizeof(Context) == 0x840); + static_assert(util::is_pod<Context>::value); + + struct StickyBits { + u8 se_security; + u8 tzram_security; + u16 crypto_security_perkey; + u8 crypto_keytable_access[AesKeySlotCount]; + u8 rsa_security_perkey; + u8 rsa_keytable_access[RsaKeySlotCount]; + }; + static_assert(util::is_pod<StickyBits>::value); + + bool ValidateStickyBits(const StickyBits &bits); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon.hpp b/libraries/libexosphere/include/exosphere/secmon.hpp new file mode 100644 index 000000000..1b476e0f6 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon.hpp @@ -0,0 +1,20 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/secmon/secmon_memory_layout.hpp> +#include <exosphere/secmon/secmon_configuration_context.hpp> +#include <exosphere/secmon/secmon_volatile_context.hpp> \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp new file mode 100644 index 000000000..7f094a14b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -0,0 +1,99 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/pkg1.hpp> +#include <exosphere/se.hpp> +#include <exosphere/secmon/secmon_monitor_context.hpp> + +namespace ams::secmon { + + struct ConfigurationContext { + union { + SecureMonitorConfiguration secmon_cfg; + u8 _raw_exosphere_config[0x80]; + }; + union { + EmummcConfiguration emummc_cfg; + u8 _raw_emummc_config[0x120]; + }; + union { + u8 _misc_data[0x400 - sizeof(_raw_exosphere_config) - sizeof(_raw_emummc_config)]; + }; + u8 sealed_device_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; + u8 sealed_master_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; + pkg1::BootConfig boot_config; + u8 rsa_private_exponents[4][se::RsaSize]; + }; + static_assert(sizeof(ConfigurationContext) == 0x1000); + static_assert(util::is_pod<ConfigurationContext>::value); + static_assert(offsetof(ConfigurationContext, sealed_device_keys) == 0x400); + + namespace impl { + + ALWAYS_INLINE uintptr_t GetConfigurationContextAddress() { + register uintptr_t x18 asm("x18"); + __asm__ __volatile__("" : [x18]"=r"(x18)); + return x18; + } + + ALWAYS_INLINE ConfigurationContext &GetConfigurationContext() { + return *reinterpret_cast<ConfigurationContext *>(GetConfigurationContextAddress()); + } + + ALWAYS_INLINE u8 *GetMasterKeyStorage(int generation) { + return GetConfigurationContext().sealed_master_keys[generation]; + } + + ALWAYS_INLINE u8 *GetDeviceMasterKeyStorage(int generation) { + return GetConfigurationContext().sealed_device_keys[generation]; + } + + ALWAYS_INLINE u8 *GetRsaPrivateExponentStorage(int which) { + return GetConfigurationContext().rsa_private_exponents[which]; + } + + ALWAYS_INLINE void SetKeyGeneration(int generation) { + GetConfigurationContext().secmon_cfg.key_generation = generation; + } + + } + + ALWAYS_INLINE const ConfigurationContext &GetConfigurationContext() { + return *reinterpret_cast<const ConfigurationContext *>(impl::GetConfigurationContextAddress()); + } + + ALWAYS_INLINE const SecureMonitorConfiguration &GetSecmonConfiguration() { + return GetConfigurationContext().secmon_cfg; + } + + ALWAYS_INLINE const EmummcConfiguration &GetEmummcConfiguration() { + return GetConfigurationContext().emummc_cfg; + } + + ALWAYS_INLINE const pkg1::BootConfig &GetBootConfig() { + return GetConfigurationContext().boot_config; + } + + ALWAYS_INLINE ams::TargetFirmware GetTargetFirmware() { + return GetSecmonConfiguration().GetTargetFirmware(); + } + + ALWAYS_INLINE int GetKeyGeneration() { + return GetSecmonConfiguration().GetKeyGeneration(); + } + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp new file mode 100644 index 000000000..c66816d5c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include <exosphere/secmon/secmon_configuration_context.arch.arm64.hpp> +#elif defined(ATMOSPHERE_ARCH_ARM) + /* Nothing to include. */ +#else + #error "Unknown architecture for secmon::ConfigurationContext.hpp" +#endif diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp new file mode 100644 index 000000000..044420e8c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp @@ -0,0 +1,73 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::secmon { + + enum EmummcType : u32 { + EmummcType_None = 0, + EmummcType_Partition = 1, + EmummcType_File = 2, + }; + + enum EmummcMmc { + EmummcMmc_Nand = 0, + EmummcMmc_Sd = 1, + EmummcMmc_Gc = 2, + }; + + constexpr inline size_t EmummcFilePathLengthMax = 0x80; + + struct EmummcFilePath { + char str[EmummcFilePathLengthMax]; + }; + static_assert(util::is_pod<EmummcFilePath>::value); + static_assert(sizeof(EmummcFilePath) == EmummcFilePathLengthMax); + + struct EmummcBaseConfiguration { + static constexpr u32 Magic = util::FourCC<'E','F','S','0'>::Code; + + u32 magic; + EmummcType type; + u32 id; + u32 fs_version; + }; + static_assert(util::is_pod<EmummcBaseConfiguration>::value); + static_assert(sizeof(EmummcBaseConfiguration) == 0x10); + + struct EmummcPartitionConfiguration { + u64 start_sector; + }; + static_assert(util::is_pod<EmummcPartitionConfiguration>::value); + + struct EmummcFileConfiguration { + EmummcFilePath path; + }; + static_assert(util::is_pod<EmummcFileConfiguration>::value); + + struct EmummcConfiguration { + EmummcBaseConfiguration base_cfg; + union { + EmummcPartitionConfiguration partition_cfg; + EmummcFileConfiguration file_cfg; + }; + EmummcFilePath emu_dir_path; + }; + static_assert(util::is_pod<EmummcConfiguration>::value); + static_assert(sizeof(EmummcConfiguration) <= 0x200); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp new file mode 100644 index 000000000..c40368a3c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -0,0 +1,277 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/mmu.hpp> + +namespace ams::secmon { + + using Address = u64; + + struct MemoryRegion { + Address start_address; + Address end_address; + + constexpr MemoryRegion(Address address, size_t size) : start_address(address), end_address(address + size) { + if (end_address < start_address) { + __builtin_unreachable(); + } + } + + constexpr Address GetStartAddress() const { + return this->start_address; + } + + constexpr Address GetAddress() const { + return this->GetStartAddress(); + } + + constexpr Address GetEndAddress() const { + return this->end_address; + } + + constexpr Address GetLastAddress() const { + return this->end_address - 1; + } + + constexpr size_t GetSize() const { + return this->end_address - this->start_address; + } + + constexpr bool Contains(Address address, size_t size) const { + return this->start_address <= address && (address + size - 1) <= this->GetLastAddress(); + } + + constexpr bool Contains(const MemoryRegion &rhs) const { + return this->Contains(rhs.GetStartAddress(), rhs.GetSize()); + } + + template<typename T = void> requires (std::is_same<T, void>::value || util::is_pod<T>::value) + ALWAYS_INLINE T *GetPointer() const { + return reinterpret_cast<T *>(this->GetAddress()); + } + + template<typename T = void> requires (std::is_same<T, void>::value || util::is_pod<T>::value) + ALWAYS_INLINE T *GetEndPointer() const { + return reinterpret_cast<T *>(this->GetEndAddress()); + } + }; + + constexpr inline const MemoryRegion MemoryRegionVirtual = MemoryRegion(UINT64_C(0x1F0000000), 2_MB); + constexpr inline const MemoryRegion MemoryRegionPhysical = MemoryRegion(UINT64_C( 0x40000000), 1_GB); + constexpr inline const MemoryRegion MemoryRegionDram = MemoryRegion(UINT64_C( 0x80000000), 2_GB); + + constexpr inline const MemoryRegion MemoryRegionDramDefaultKernelCarveout = MemoryRegion(UINT64_C(0x80060000), UINT64_C(0x1FFE0000)); + static_assert(MemoryRegionDram.Contains(MemoryRegionDramDefaultKernelCarveout)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalIram = MemoryRegion(UINT64_C(0x40000000), 0x40000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzram = MemoryRegion(UINT64_C(0x7C010000), 0x10000); + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalIram)); + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalTzram)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatile(UINT64_C(0x7C010000), 0x2000); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramVolatile)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramNonVolatile(UINT64_C(0x7C012000), 0xE000); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramNonVolatile)); + + static_assert(MemoryRegionPhysicalTzram.GetSize() == MemoryRegionPhysicalTzramNonVolatile.GetSize() + MemoryRegionPhysicalTzramVolatile.GetSize()); + + constexpr inline const MemoryRegion MemoryRegionVirtualL1 = MemoryRegion(util::AlignDown(MemoryRegionVirtual.GetAddress(), mmu::L1EntrySize), mmu::L1EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalL1 = MemoryRegion(util::AlignDown(MemoryRegionPhysical.GetAddress(), mmu::L1EntrySize), mmu::L1EntrySize); + static_assert(MemoryRegionVirtualL1.Contains(MemoryRegionVirtual)); + static_assert(MemoryRegionPhysicalL1.Contains(MemoryRegionPhysical)); + + constexpr inline const MemoryRegion MemoryRegionVirtualL2 = MemoryRegion(util::AlignDown(MemoryRegionVirtual.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramL2 = MemoryRegion(util::AlignDown(MemoryRegionPhysicalIram.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL2 = MemoryRegion(util::AlignDown(MemoryRegionPhysicalTzram.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + static_assert(MemoryRegionVirtualL2.Contains(MemoryRegionVirtual)); + static_assert(MemoryRegionPhysicalIramL2.Contains(MemoryRegionPhysicalIram)); + static_assert(MemoryRegionPhysicalTzramL2.Contains(MemoryRegionPhysicalTzram)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000)); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0); + + #define AMS_SECMON_FOREACH_DEVICE_REGION(HANDLER, ...) \ + HANDLER(GicDistributor, Empty, UINT64_C(0x50041000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(GicCpuInterface, GicDistributor, UINT64_C(0x50042000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(Uart, GicCpuInterface, UINT64_C(0x70006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \ + HANDLER(ClkRst, Uart, UINT64_C(0x60006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \ + HANDLER(RtcPmc, ClkRst, UINT64_C(0x7000E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(Timer, RtcPmc, UINT64_C(0x60005000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(System, Timer, UINT64_C(0x6000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(SecurityEngine, System, UINT64_C(0x70012000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(SecurityEngine2, SecurityEngine, UINT64_C(0x70412000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(SysCtr0, SecurityEngine2, UINT64_C(0x700F0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(MemoryController, SysCtr0, UINT64_C(0x70019000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(FuseKFuse, MemoryController, UINT64_C(0x7000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(ApbMisc, FuseKFuse, UINT64_C(0x70000000), UINT64_C(0x4000), true, ## __VA_ARGS__) \ + HANDLER(FlowController, ApbMisc, UINT64_C(0x60007000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(BootloaderParams, FlowController, UINT64_C(0x40000000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(I2c5, BootloaderParams, UINT64_C(0x7000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(Gpio, I2c5, UINT64_C(0x6000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(I2c1, Gpio, UINT64_C(0x7000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(ExceptionVectors, I2c1, UINT64_C(0x6000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(MemoryController0, ExceptionVectors, UINT64_C(0x7001C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), UINT64_C(0x1000), true, ## __VA_ARGS__) + + #define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \ + constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \ + constexpr inline const MemoryRegion MemoryRegionPhysicalDevice##_NAME_ = MemoryRegion(_ADDRESS_, _SIZE_); \ + static_assert(MemoryRegionVirtualDevice.Contains(MemoryRegionVirtualDevice##_NAME_)); \ + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalDevice##_NAME_)); + + AMS_SECMON_FOREACH_DEVICE_REGION(DEFINE_DEVICE_REGION) + + #undef DEFINE_DEVICE_REGION + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceFuses = MemoryRegion(MemoryRegionVirtualDeviceFuseKFuse.GetAddress() + 0x800, 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceFuses = MemoryRegion(MemoryRegionPhysicalDeviceFuseKFuse.GetAddress() + 0x800, 0x400); + static_assert(MemoryRegionVirtualDeviceFuseKFuse.Contains(MemoryRegionVirtualDeviceFuses)); + static_assert(MemoryRegionPhysicalDeviceFuseKFuse.Contains(MemoryRegionPhysicalDeviceFuses)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceActivityMonitor = MemoryRegion(MemoryRegionVirtualDeviceSystem.GetAddress() + 0x800, 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceActivityMonitor = MemoryRegion(MemoryRegionPhysicalDeviceSystem.GetAddress() + 0x800, 0x400); + static_assert(MemoryRegionVirtualDeviceSystem.Contains(MemoryRegionVirtualDeviceActivityMonitor)); + static_assert(MemoryRegionPhysicalDeviceSystem.Contains(MemoryRegionPhysicalDeviceActivityMonitor)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartA = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x000, 0x040); + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartB = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x040, 0x040); + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartC = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x200, 0x100); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartA)); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartB)); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartC)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartA = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x000, 0x040); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartB = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x040, 0x040); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartC = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x200, 0x100); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartA)); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartB)); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartC)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDevicePmc = MemoryRegion(MemoryRegionVirtualDeviceRtcPmc.GetAddress() + 0x400, 0xC00); + constexpr inline const MemoryRegion MemoryRegionPhysicalDevicePmc = MemoryRegion(MemoryRegionPhysicalDeviceRtcPmc.GetAddress() + 0x400, 0xC00); + static_assert(MemoryRegionVirtualDeviceRtcPmc.Contains(MemoryRegionVirtualDevicePmc)); + static_assert(MemoryRegionPhysicalDeviceRtcPmc.Contains(MemoryRegionPhysicalDevicePmc)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramReadOnlyAlias = MemoryRegion(UINT64_C(0x1F00A0000), MemoryRegionPhysicalTzram.GetSize()); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramReadOnlyAlias = MemoryRegion(MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize()); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramReadOnlyAlias)); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramReadOnlyAlias)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgram(UINT64_C(0x1F00C0000), 0xC000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramProgram)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramExceptionVectors(UINT64_C(0x1F00C0000), 0x800); + static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramExceptionVectors)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramMain(UINT64_C(0x1F00C0800), 0xB800); + static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramMain)); + + static_assert(MemoryRegionVirtualTzramProgram.GetSize() == MemoryRegionVirtualTzramProgramExceptionVectors.GetSize() + MemoryRegionVirtualTzramProgramMain.GetSize()); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramProgram(UINT64_C(0x7C012000), 0xC000); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramProgram)); + + constexpr inline const Address PhysicalTzramProgramResetVector = MemoryRegionPhysicalTzramProgram.GetAddress() + MemoryRegionVirtualTzramProgramExceptionVectors.GetSize(); + static_assert(static_cast<u32>(PhysicalTzramProgramResetVector) == PhysicalTzramProgramResetVector); + + constexpr uintptr_t GetPhysicalTzramProgramAddress(uintptr_t virtual_address) { + return virtual_address - MemoryRegionVirtualTzramProgram.GetStartAddress() + MemoryRegionPhysicalTzramNonVolatile.GetStartAddress(); + } + + constexpr inline const MemoryRegion MemoryRegionVirtualIramSc7Work = MemoryRegion(UINT64_C(0x1F0120000), MemoryRegionPhysicalTzram.GetSize()); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramSc7Work = MemoryRegion( UINT64_C(0x40020000), MemoryRegionPhysicalTzram.GetSize()); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Work)); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Work)); + + constexpr inline const MemoryRegion MemoryRegionVirtualIramSc7Firmware = MemoryRegion(UINT64_C(0x1F0140000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramSc7Firmware = MemoryRegion( UINT64_C(0x40003000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Firmware)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDebug = MemoryRegion(UINT64_C(0x1F0160000), 0x10000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramBootCode = MemoryRegion(UINT64_C(0x1F01C0000), 0x2000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramBootCode = MemoryRegion( UINT64_C(0x7C010000), 0x2000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramBootCode)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramBootCode)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDramMonitorConfiguration = MemoryRegion( UINT64_C(0x8000F000), 0x1000); + + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStore = MemoryRegion(UINT64_C(0x1F0100000), 0x10000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStore = MemoryRegion( UINT64_C(0x80010000), 0x10000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDramSecureDataStore)); + static_assert(MemoryRegionDram.Contains(MemoryRegionPhysicalDramSecureDataStore)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreTzram = MemoryRegion(UINT64_C(0x1F0100000), 0xE000); + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware = MemoryRegion(UINT64_C(0x1F010E000), 0x17C0); + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreSecurityEngineState = MemoryRegion(UINT64_C(0x1F010F7C0), 0x0840); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreTzram)); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware)); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreTzram = MemoryRegion(UINT64_C(0x80010000), 0xE000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware = MemoryRegion(UINT64_C(0x8001E000), 0x17C0); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState = MemoryRegion(UINT64_C(0x8001F7C0), 0x0840); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreTzram)); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware)); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState)); + + constexpr inline const MemoryRegion MemoryRegionVirtualAtmosphereIramPage = MemoryRegion(UINT64_C(0x1F01F0000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualAtmosphereIramPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualAtmosphereUserPage = MemoryRegion(UINT64_C(0x1F01F2000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualAtmosphereUserPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualSmcUserPage = MemoryRegion(UINT64_C(0x1F01F4000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualSmcUserPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramVolatileData = MemoryRegion(UINT64_C(0x1F01F6000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatileData = MemoryRegion( UINT64_C(0x7C010000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramVolatileData)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramVolatileData)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramVolatileStack = MemoryRegion(UINT64_C(0x1F01F8000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatileStack = MemoryRegion( UINT64_C(0x7C011000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramVolatileStack)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramVolatileStack)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramConfigurationData = MemoryRegion(UINT64_C(0x1F01FA000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramConfigurationData = MemoryRegion( UINT64_C(0x7C01E000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramConfigurationData)); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramConfigurationData)); + + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramL1PageTable = MemoryRegion(UINT64_C(0x1F01FCFC0), 0x40); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL1PageTable = MemoryRegion( UINT64_C(0x7C01EFC0), 0x40); + static_assert(MemoryRegionPhysicalTzramConfigurationData.Contains(MemoryRegionPhysicalTzramL1PageTable)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramL2L3PageTable = MemoryRegion(UINT64_C(0x1F01FE000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL2L3PageTable = MemoryRegion( UINT64_C(0x7C01F000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramL2L3PageTable)); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramL2L3PageTable)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(0x7C010800, 0xD800); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(0x40032000, 0xC000); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp new file mode 100644 index 000000000..1aa0b679d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -0,0 +1,77 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/secmon/secmon_emummc_context.hpp> + +namespace ams::secmon { + + enum SecureMonitorConfigurationFlag : u32 { + SecureMonitorConfigurationFlag_None = (0u << 0), + SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel = (1u << 1), + SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForUser = (1u << 2), + SecureMonitorConfigurationFlag_DisableUserModeExceptionHandlers = (1u << 3), + SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4), + SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), + SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), + + SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, + }; + + struct SecureMonitorStorageConfiguration { + static constexpr u32 Magic = util::FourCC<'E','X','O','0'>::Code; + + u32 magic; + ams::TargetFirmware target_firmware; + u32 flags; + u32 reserved[5]; + EmummcConfiguration emummc_cfg; + + constexpr bool IsValid() const { return this->magic == Magic; } + }; + static_assert(util::is_pod<SecureMonitorStorageConfiguration>::value); + static_assert(sizeof(SecureMonitorStorageConfiguration) == 0x130); + + struct SecureMonitorConfiguration { + ams::TargetFirmware target_firmware; + s32 key_generation; + u32 flags; + u32 reserved[(0x80 - 0x0C) / sizeof(u32)]; + + constexpr void CopyFrom(const SecureMonitorStorageConfiguration &storage) { + this->target_firmware = storage.target_firmware; + this->flags = storage.flags; + } + + constexpr ams::TargetFirmware GetTargetFirmware() const { return this->target_firmware; } + constexpr int GetKeyGeneration() const { return this->key_generation; } + + constexpr bool IsDevelopmentFunctionEnabledForKernel() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel) != 0; } + constexpr bool IsDevelopmentFunctionEnabledForUser() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForUser) != 0; } + constexpr bool DisableUserModeExceptionHandlers() const { return (this->flags & SecureMonitorConfigurationFlag_DisableUserModeExceptionHandlers) != 0; } + constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } + constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } + constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + }; + static_assert(util::is_pod<SecureMonitorConfiguration>::value); + static_assert(sizeof(SecureMonitorConfiguration) == 0x80); + + constexpr inline const SecureMonitorConfiguration DefaultSecureMonitorConfiguration = { + .target_firmware = ams::TargetFirmware_Current, + .flags = SecureMonitorConfigurationFlag_Default, + }; + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp new file mode 100644 index 000000000..c8bc4e37b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp @@ -0,0 +1,62 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::secmon { + + /* The VolatileStack page is reserved entirely for use for core 3 SMC handling. */ + constexpr inline const Address Core3SmcStackAddress = MemoryRegionVirtualTzramVolatileStack.GetAddress() + MemoryRegionVirtualTzramVolatileStack.GetSize(); + + constexpr inline const size_t CoreExceptionStackSize = 0x80; + + /* Nintendo uses the bottom 0x740 of this as a stack for warmboot setup, and another 0x740 for the core 0/1/2 SMC stacks. */ + /* This is...wasteful. The warmboot stack is not deep. We will thus save 1K+ of nonvolatile storage by keeping the random cache in here. */ + struct VolatileData { + u8 random_cache[0x400]; + u8 se_work_block[crypto::AesEncryptor128::BlockSize]; + u8 reserved_danger_zone[0x30]; /* This memory is "available", but careful consideration must be taken before declaring it used. */ + u8 warmboot_stack[0x380]; + u8 core012_smc_stack[0x6C0]; + u8 core_exception_stacks[3][CoreExceptionStackSize]; + }; + static_assert(util::is_pod<VolatileData>::value); + static_assert(sizeof(VolatileData) == 0x1000); + + ALWAYS_INLINE VolatileData &GetVolatileData() { + return *MemoryRegionVirtualTzramVolatileData.GetPointer<VolatileData>(); + } + + ALWAYS_INLINE u8 *GetRandomBytesCache() { + return GetVolatileData().random_cache; + } + + constexpr ALWAYS_INLINE size_t GetRandomBytesCacheSize() { + return sizeof(VolatileData::random_cache); + } + + ALWAYS_INLINE u8 *GetSecurityEngineEphemeralWorkBlock() { + return GetVolatileData().se_work_block; + } + + constexpr inline const Address WarmbootStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, warmboot_stack) + sizeof(VolatileData::warmboot_stack); + constexpr inline const Address Core012SmcStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, core012_smc_stack) + sizeof(VolatileData::core012_smc_stack); + + constexpr inline const Address Core0ExceptionStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, core_exception_stacks) + CoreExceptionStackSize; + constexpr inline const Address Core1ExceptionStackAddress = Core0ExceptionStackAddress + CoreExceptionStackSize; + constexpr inline const Address Core2ExceptionStackAddress = Core1ExceptionStackAddress + CoreExceptionStackSize; + +} diff --git a/libraries/libexosphere/include/exosphere/tegra.hpp b/libraries/libexosphere/include/exosphere/tegra.hpp new file mode 100644 index 000000000..370bcc3bc --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#include <exosphere/tegra/tegra_ahb_arbc.hpp> +#include <exosphere/tegra/tegra_apb_misc.hpp> +#include <exosphere/tegra/tegra_avp_cache.hpp> +#include <exosphere/tegra/tegra_emc.hpp> +#include <exosphere/tegra/tegra_evp.hpp> +#include <exosphere/tegra/tegra_flow_ctlr.hpp> +#include <exosphere/tegra/tegra_ictlr.hpp> +#include <exosphere/tegra/tegra_mc.hpp> +#include <exosphere/tegra/tegra_mselect.hpp> +#include <exosphere/tegra/tegra_pmc.hpp> +#include <exosphere/tegra/tegra_sb.hpp> +#include <exosphere/tegra/tegra_sysctr0.hpp> +#include <exosphere/tegra/tegra_timer.hpp> diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp new file mode 100644 index 000000000..a131cf68d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define AHB_ARBC(x) (0x6000c000 + x) + +#define AHB_ARBITRATION_DISABLE (0x004) +#define AHB_ARBITRATION_PRIORITY_CTRL (0x008) +#define AHB_MASTER_SWID (0x018) +#define AHB_MASTER_SWID_1 (0x038) +#define AHB_GIZMO_TZRAM (0x054) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp new file mode 100644 index 000000000..af05f3658 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp @@ -0,0 +1,99 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 (0xc04) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 (0xc08) + +#define AHB_MISC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AHB_MISC, NAME) +#define AHB_MISC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AHB_MISC, NAME, VALUE) +#define AHB_MISC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AHB_MISC, NAME, ENUM) +#define AHB_MISC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AHB_MISC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_AHB_MISC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AHB_MISC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_AHB_MISC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_AHB_MISC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_AHB_MISC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_AHB_MISC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_AHB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE) + +DEFINE_SLAVE_SECURITY_REG(0, 29, STM); +DEFINE_SLAVE_SECURITY_REG(0, 24, CEC); +DEFINE_SLAVE_SECURITY_REG(0, 23, ATOMICS); +DEFINE_SLAVE_SECURITY_REG(0, 22, LA); +DEFINE_SLAVE_SECURITY_REG(0, 21, HDA); +DEFINE_SLAVE_SECURITY_REG(0, 20, SATA); +DEFINE_SLAVE_SECURITY_REG(0, 16, KFUSE); +DEFINE_SLAVE_SECURITY_REG(0, 15, FUSE); +DEFINE_SLAVE_SECURITY_REG(0, 14, SE); +DEFINE_SLAVE_SECURITY_REG(0, 13, PMC); +DEFINE_SLAVE_SECURITY_REG(0, 11, RTC); +DEFINE_SLAVE_SECURITY_REG(0, 10, CSITE); +DEFINE_SLAVE_SECURITY_REG(0, 9, QSPI); +DEFINE_SLAVE_SECURITY_REG(0, 8, PWM); +DEFINE_SLAVE_SECURITY_REG(0, 6, DTV); +DEFINE_SLAVE_SECURITY_REG(0, 4, APE); +DEFINE_SLAVE_SECURITY_REG(0, 3, PINMUX_AUX); +DEFINE_SLAVE_SECURITY_REG(0, 2, SATA_AUX); +DEFINE_SLAVE_SECURITY_REG(0, 1, MISC_REGS); + +DEFINE_SLAVE_SECURITY_REG(1, 31, I2C6); +DEFINE_SLAVE_SECURITY_REG(1, 30, DVC); +DEFINE_SLAVE_SECURITY_REG(1, 29, I2C4); +DEFINE_SLAVE_SECURITY_REG(1, 28, I2C3); +DEFINE_SLAVE_SECURITY_REG(1, 27, I2C2); +DEFINE_SLAVE_SECURITY_REG(1, 26, I2C1); +DEFINE_SLAVE_SECURITY_REG(1, 25, SPI6); +DEFINE_SLAVE_SECURITY_REG(1, 24, SPI5); +DEFINE_SLAVE_SECURITY_REG(1, 23, SPI4); +DEFINE_SLAVE_SECURITY_REG(1, 22, SPI3); +DEFINE_SLAVE_SECURITY_REG(1, 21, SPI2); +DEFINE_SLAVE_SECURITY_REG(1, 20, SPI1); +DEFINE_SLAVE_SECURITY_REG(1, 15, UART_D); +DEFINE_SLAVE_SECURITY_REG(1, 14, UART_C); +DEFINE_SLAVE_SECURITY_REG(1, 13, UART_B); +DEFINE_SLAVE_SECURITY_REG(1, 12, UART_A); +DEFINE_SLAVE_SECURITY_REG(1, 11, EMCB); +DEFINE_SLAVE_SECURITY_REG(1, 10, MCB); +DEFINE_SLAVE_SECURITY_REG(1, 9, EMC1); +DEFINE_SLAVE_SECURITY_REG(1, 8, MC1); +DEFINE_SLAVE_SECURITY_REG(1, 5, EMC0); +DEFINE_SLAVE_SECURITY_REG(1, 4, MC0); + +DEFINE_SLAVE_SECURITY_REG(2, 21, FEK); +DEFINE_SLAVE_SECURITY_REG(2, 20, PKA1); +DEFINE_SLAVE_SECURITY_REG(2, 19, SE2); +DEFINE_SLAVE_SECURITY_REG(2, 16, DVFS); +DEFINE_SLAVE_SECURITY_REG(2, 15, MIPI_CAL); +DEFINE_SLAVE_SECURITY_REG(2, 14, XUSB_PADCTL); +DEFINE_SLAVE_SECURITY_REG(2, 13, XUSB_DEV); +DEFINE_SLAVE_SECURITY_REG(2, 12, XUSB_HOST); +DEFINE_SLAVE_SECURITY_REG(2, 11, APB2JTAG); +DEFINE_SLAVE_SECURITY_REG(2, 10, SOC_THERM); +DEFINE_SLAVE_SECURITY_REG(2, 9, DP2); +DEFINE_SLAVE_SECURITY_REG(2, 8, DDS); +DEFINE_SLAVE_SECURITY_REG(2, 7, MIPIBIF); +DEFINE_SLAVE_SECURITY_REG(2, 3, SDMMC4); +DEFINE_SLAVE_SECURITY_REG(2, 2, SDMMC3); +DEFINE_SLAVE_SECURITY_REG(2, 1, SDMMC2); +DEFINE_SLAVE_SECURITY_REG(2, 0, SDMMC1); + +#undef DEFINE_SLAVE_SECURITY_REG + +#define SLAVE_SECURITY_REG_BITS_ENUM(RINDEX, NAME, ENUM) AHB_MISC_REG_BITS_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, ENUM) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp new file mode 100644 index 000000000..52e4c7748 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp @@ -0,0 +1,35 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define AVP_CACHE_ADDRESS(x) (0x50040000 + x) + +#define AVP_CACHE_CONFIG (0x000) + +#define AVP_CACHE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AVP_CACHE, NAME) +#define AVP_CACHE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AVP_CACHE, NAME, VALUE) +#define AVP_CACHE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AVP_CACHE, NAME, ENUM) +#define AVP_CACHE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AVP_CACHE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_AVP_CACHE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AVP_CACHE, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_AVP_CACHE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_AVP_CACHE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_AVP_CACHE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_WB, 10, FALSE, TRUE); +DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_RB, 11, FALSE, TRUE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp new file mode 100644 index 000000000..f072b0196 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp @@ -0,0 +1,90 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define EMC_ADDRESS(x) (0x7001B000 + x) +#define EMC0_ADDRESS(x) (0x7001E000 + x) +#define EMC1_ADDRESS(x) (0x7001F000 + x) + +#define EMC_CFG (0x00C) +#define EMC_ADR_CFG (0x010) +#define EMC_TIMING_CONTROL (0x028) +#define EMC_SELF_REF (0x0E0) +#define EMC_MRW (0x0E8) +#define EMC_FBIO_CFG5 (0x104) +#define EMC_MRW3 (0x138) +#define EMC_AUTO_CAL_CONFIG (0x2A4) +#define EMC_REQ_CTRL (0x2B0) +#define EMC_EMC_STATUS (0x2B4) +#define EMC_CFG_DIG_DLL (0x2BC) +#define EMC_ZCAL_INTERVAL (0x2E0) +#define EMC_PMC_SCRATCH3 (0x448) +#define EMC_FBIO_CFG7 (0x584) + +#define EMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (EMC, NAME) +#define EMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (EMC, NAME, VALUE) +#define EMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (EMC, NAME, ENUM) +#define EMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(EMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_EMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (EMC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_EMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_EMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_EMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_EMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_EMC_REG_BIT_ENUM(CFG_DYN_SELF_REF, 28, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(CFG_DRAM_ACPD, 29, NO_POWERDOWN, ACTIVE_POWERDOWN); + +DEFINE_EMC_REG_BIT_ENUM(ADR_CFG_EMEM_NUMDEV, 0, N1, N2); + +DEFINE_EMC_REG_BIT_ENUM(TIMING_CONTROL_TIMING_UPDATE, 0, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(SELF_REF_SELF_REF_CMD, 0, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(SELF_REF_ACTIVE_SELF_REF, 8, DISABLED, ENABLED); +DEFINE_EMC_REG_TWO_BIT_ENUM(SELF_REF_SREF_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED); + +DEFINE_EMC_REG(MRW_OP, 0, 8); +DEFINE_EMC_REG(MRW_MA, 16, 8); +DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_CNT, 26, SHORT, LONG, EXT1, EXT2); +DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED); + +DEFINE_EMC_REG_TWO_BIT_ENUM(FBIO_CFG5_DRAM_TYPE, 0, DDR4, LPDDR4, LPDDR2, DDR2); + +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, 9, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, 10, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLE, ENABLE); + +DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_READS, 0, 1); +DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_WRITES, 1, 1); + +DEFINE_EMC_REG_TWO_BIT_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, 8, DISABLED, DEV0_ENABLED, DEV1_ENABLED, BOTH_ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, 8, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, 2, WAITING, COMPLETED); +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, 23, DONE, BUSY); + +DEFINE_EMC_REG_BIT_ENUM(CFG_DIG_DLL_CFG_DLL_EN, 0, DISABLED, ENABLED); + +DEFINE_EMC_REG(ZCAL_INTERVAL_LO, 0, 10); +DEFINE_EMC_REG(ZCAL_INTERVAL_HI, 10, 14); + +DEFINE_EMC_REG(PMC_SCRATCH3_DDR_CNTRL, 0, 19); +DEFINE_EMC_REG_BIT_ENUM(PMC_SCRATCH3_WEAK_BIAS, 30, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(FBIO_CFG7_CH1_ENABLE, 2, DISABLE, ENABLE); + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp new file mode 100644 index 000000000..27d61f7cf --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp @@ -0,0 +1,19 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define EVP_CPU_RESET_VECTOR (0x100) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp new file mode 100644 index 000000000..d7d3361e1 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -0,0 +1,38 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + + +#define FLOW_CTLR_FLOW_DBG_QUAL (0x050) +#define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098) + +#define FLOW_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FLOW_CTLR, NAME) +#define FLOW_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FLOW_CTLR, NAME, VALUE) +#define FLOW_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FLOW_CTLR, NAME, ENUM) +#define FLOW_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FLOW_CTLR, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_FLOW_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FLOW_CTLR, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_FLOW_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_FLOW_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_FLOW_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_FLOW_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); + +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, 0, FAST, SLOW); +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, 1, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, 2, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp new file mode 100644 index 000000000..6f11ee918 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp @@ -0,0 +1,27 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define PRI_ICTLR(n) (0x60004000 + n) +#define SEC_ICTLR(n) (0x60004100 + n) +#define TRI_ICTLR(n) (0x60004200 + n) +#define QUAD_ICTLR(n) (0x60004300 + n) +#define PENTA_ICTLR(n) (0x60004400 + n) +#define HEXA_ICTLR(n) (0x60004500 + n) + +#define ICTLR_COP_IER_CLR (0x038) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp new file mode 100644 index 000000000..b97b5c631 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp @@ -0,0 +1,329 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/reg.hpp> + +#define MC_INTSTATUS (0x000) +#define MC_INTMASK (0x004) +#define MC_ERR_STATUS (0x008) +#define MC_ERR_ADR (0x00c) +#define MC_SMMU_CONFIG (0x010) +#define MC_SMMU_TLB_CONFIG (0x014) +#define MC_SMMU_PTC_CONFIG (0x018) +#define MC_SMMU_PTB_ASID (0x01c) +#define MC_SMMU_PTB_DATA (0x020) +#define MC_SMMU_TLB_FLUSH (0x030) +#define MC_SMMU_PTC_FLUSH (0x034) + +#define MC_SECURITY_CFG0 (0x070) +#define MC_SECURITY_CFG1 (0x074) +#define MC_SECURITY_CFG3 (0x9BC) + +#define MC_SMMU_TRANSLATION_ENABLE_0 (0x228) +#define MC_SMMU_TRANSLATION_ENABLE_1 (0x22C) +#define MC_SMMU_TRANSLATION_ENABLE_2 (0x230) +#define MC_SMMU_TRANSLATION_ENABLE_3 (0x234) +#define MC_SMMU_TRANSLATION_ENABLE_4 (0xB98) + +#define MC_SMMU_ASID_SECURITY (0x038) +#define MC_SMMU_ASID_SECURITY_1 (0x03c) +#define MC_SMMU_ASID_SECURITY_2 (0x9e0) +#define MC_SMMU_ASID_SECURITY_3 (0x9e4) +#define MC_SMMU_ASID_SECURITY_4 (0x9e8) +#define MC_SMMU_ASID_SECURITY_5 (0x9ec) +#define MC_SMMU_ASID_SECURITY_6 (0x9f0) +#define MC_SMMU_ASID_SECURITY_7 (0x9f4) + +#define MC_SEC_CARVEOUT_BOM (0x670) +#define MC_SEC_CARVEOUT_SIZE_MB (0x674) +#define MC_SEC_CARVEOUT_REG_CTRL (0x678) + +#define MC_VIDEO_PROTECT_BOM (0x648) +#define MC_VIDEO_PROTECT_SIZE_MB (0x64c) +#define MC_VIDEO_PROTECT_REG_CTRL (0x650) +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 (0x984) +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 (0x988) + +#define MC_MTS_CARVEOUT_BOM (0x9a0) +#define MC_MTS_CARVEOUT_SIZE_MB (0x9a4) +#define MC_MTS_CARVEOUT_ADR_HI (0x9a8) +#define MC_MTS_CARVEOUT_REG_CTRL (0x9ac) + +#define MC_SECURITY_CARVEOUT1_CFG0 (0xc08) +#define MC_SECURITY_CARVEOUT1_BOM (0xc0c) +#define MC_SECURITY_CARVEOUT1_BOM_HI (0xc10) +#define MC_SECURITY_CARVEOUT1_SIZE_128KB (0xc14) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 (0xc18) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 (0xc1c) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 (0xc20) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 (0xc24) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 (0xc28) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc2c) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc30) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc34) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc38) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc3c) + +#define MC_SECURITY_CARVEOUT2_CFG0 (0xc58) +#define MC_SECURITY_CARVEOUT2_BOM (0xc5c) +#define MC_SECURITY_CARVEOUT2_BOM_HI (0xc60) +#define MC_SECURITY_CARVEOUT2_SIZE_128KB (0xc64) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 (0xc68) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 (0xc6c) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 (0xc70) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 (0xc74) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 (0xc78) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc7c) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc80) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc84) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc88) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc8c) + +#define MC_SECURITY_CARVEOUT3_CFG0 (0xca8) +#define MC_SECURITY_CARVEOUT3_BOM (0xcac) +#define MC_SECURITY_CARVEOUT3_BOM_HI (0xcb0) +#define MC_SECURITY_CARVEOUT3_SIZE_128KB (0xcb4) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 (0xcb8) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 (0xcbc) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 (0xcc0) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 (0xcc4) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 (0xcc8) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 (0xccc) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 (0xcd0) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 (0xcd4) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 (0xcd8) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 (0xcdc) + +#define MC_SECURITY_CARVEOUT4_CFG0 (0xcf8) +#define MC_SECURITY_CARVEOUT4_BOM (0xcfc) +#define MC_SECURITY_CARVEOUT4_BOM_HI (0xd00) +#define MC_SECURITY_CARVEOUT4_SIZE_128KB (0xd04) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 (0xd08) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 (0xd0c) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 (0xd10) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 (0xd14) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 (0xd18) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd1c) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd20) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd24) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd28) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd2c) + +#define MC_SECURITY_CARVEOUT5_CFG0 (0xd48) +#define MC_SECURITY_CARVEOUT5_BOM (0xd4c) +#define MC_SECURITY_CARVEOUT5_BOM_HI (0xd50) +#define MC_SECURITY_CARVEOUT5_SIZE_128KB (0xd54) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 (0xd58) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 (0xd5c) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 (0xd60) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 (0xd64) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 (0xd68) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd6c) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd70) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd74) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd78) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd7c) + + +#define MC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MC, NAME) +#define MC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MC, NAME, VALUE) +#define MC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MC, NAME, ENUM) +#define MC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_MC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_MC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_MC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_MC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_MC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_MC_REG_BIT_ENUM(SMMU_CONFIG_SMMU_ENABLE, 0, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0, 6); +DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, 28, DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, 29, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0, 7); +DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 24, 4); +DEFINE_MC_REG_BIT_ENUM(SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, 29, DISABLE, ENABLE); + +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_0, 0, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, 1, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, 2, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, 3, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_4, 4, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_5, 5, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_6, 6, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_7, 7, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_8, 8, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_9, 9, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_10, 10, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_11, 11, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_12, 12, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_13, 13, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_14, 14, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_15, 15, NONSECURE, SECURE); + +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_0, 16, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_1, 17, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_2, 18, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_3, 19, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_4, 20, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_5, 21, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_6, 22, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_7, 23, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_8, 24, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_9, 25, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_10, 26, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_11, 27, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_12, 28, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_13, 29, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_14, 30, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_15, 31, NONPROMOTING, PROMOTING); + +DEFINE_MC_REG(SECURITY_CFG0_SECURITY_BOM, 20, 12); +DEFINE_MC_REG(SECURITY_CFG1_SECURITY_SIZE, 0, 13); +DEFINE_MC_REG(SECURITY_CFG3_SECURITY_BOM_HI, 0, 2); + +DEFINE_MC_REG_BIT_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED); + +DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, 0, ENABLED, DISABLED); +DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, 1, DISABLED, ENABLED); + +DEFINE_MC_REG_BIT_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED); + +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_PROTECT_MODE, 0, LOCKBIT_SECURE, TZ_SECURE); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_LOCK_MODE, 1, UNLOCKED, LOCKED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, 2, ANY_ADDRESS, UNTRANSLATED_ONLY); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, 3, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, 4, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, 5, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, 6, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, 7, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, 8, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, 9, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, 10, DISABLED, ENABLED); +DEFINE_MC_REG(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 11, 3); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, 14, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, 15, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, 16, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, 17, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, 18, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, 19, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, 20, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, 21, ENABLE_CHECKS, DISABLE_CHECKS); + +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, 22, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, 23, DISABLED, BYPASS_CHECK); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, 24, DISABLED, BYPASS_CHECK); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, 25, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, 26, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_IS_WPR, 27, DISABLED, ENABLED); + +#define MC_CLIENT_ACCESS_NUM_CLIENTS 32 + +/* _ACCESS0 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PTCR, ( 0 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0A, ( 1 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0AB, ( 2 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0B, ( 3 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0BB, ( 4 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0C, ( 5 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0CB, ( 6 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AFIR, ( 14 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AVPCARM7R, ( 15 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHC, ( 16 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHCB, ( 17 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HDAR, ( 21 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XDMAR, ( 22 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XR, ( 23 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_NVENCSRD, ( 28 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBDMAR, ( 29 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBSLVR, ( 30 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_SATAR, ( 31 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); + +/* _ACCESS1 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVR, ( 34 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMBER, ( 35 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMCER, ( 36 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDETPER, ( 37 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPR, ( 38 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORER, ( 39 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_NVENCSWR, ( 43 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AFIW, ( 49 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AVPCARM7W, ( 50 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HDAW, ( 53 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HOST1XW, ( 54 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPW, ( 56 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCOREW, ( 57 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBDMAW, ( 59 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBSLVW, ( 60 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_SATAW, ( 61 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVW, ( 62 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEDBGW, ( 63 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); + +/* _ACCESS2 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDEMBEW, ( 64 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDETPMW, ( 65 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRA, ( 68 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWA, ( 70 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWB, ( 71 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTR, ( 74 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTW, ( 75 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVR, ( 76 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVW, ( 77 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRAB, ( 78 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWAB, ( 80 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWBB, ( 81 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSRD, ( 84 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSWR, ( 85 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCR, ( 86 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCW, ( 87 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSRD, ( 88 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSWR, ( 89 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_DISPLAYT, ( 90 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); + +/* _ACCESS3 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRA, ( 96 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAA, ( 97 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCR, ( 98 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAB, ( 99 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWA, (100 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAA, (101 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCW, (102 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAB, (103 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSRD, (108 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSWR, (109 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VIW, (114 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_DISPLAYD, (115 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSRD, (120 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSWR, (121 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APER, (122 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APEW, (123 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSRD, (126 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSWR, (127 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); + +/* _ACCESS4 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESRD, (128 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESWR, (129 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPR, (130 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPW, (131 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRR, (132 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRW, (133 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECRDB, (134 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECWRB, (135 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSRD2, (136 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSWR2, (137 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp new file mode 100644 index 000000000..20c5530df --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp @@ -0,0 +1,22 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define MSELECT(x) (0x50060000 + x) + +#define MSELECT_CONFIG (0x000) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp new file mode 100644 index 000000000..ee10391fa --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -0,0 +1,156 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> +#include <exosphere/reg.hpp> + +#define APBDEV_PMC_CNTRL (0x000) +#define APBDEV_PMC_DPD_SAMPLE (0x020) +#define APBDEV_PMC_DPD_ENABLE (0x024) +#define APBDEV_PMC_CLAMP_STATUS (0x02C) +#define APBDEV_PMC_PWRGATE_TOGGLE (0x030) +#define APBDEV_PMC_PWRGATE_STATUS (0x038) +#define APBDEV_PMC_SCRATCH0 (0x050) +#define APBDEV_PMC_SCRATCH1 (0x054) +#define APBDEV_PMC_SCRATCH12 (0x080) +#define APBDEV_PMC_SCRATCH13 (0x084) +#define APBDEV_PMC_SCRATCH18 (0x098) +#define APBDEV_PMC_SCRATCH20 (0x0A0) +#define APBDEV_PMC_CRYPTO_OP (0x0F4) +#define APBDEV_PM (0x014) +#define APBDEV_PMC_WAKE2_STATUS (0x168) +#define APBDEV_PMC_WEAK_BIAS (0x2C8) +#define APBDEV_PMC_CNTRL2 (0x440) +#define APBDEV_PMC_FUSE_CTRL (0x450) +#define APBDEV_PMC_IO_DPD3_REQ (0x45C) +#define APBDEV_PMC_IO_DPD3_STATUS (0x460) +#define APBDEV_PMC_IO_DPD4_REQ (0x464) +#define APBDEV_PMC_IO_DPD4_STATUS (0x468) +#define APBDEV_PMC_SET_SW_CLAMP (0x47C) +#define APBDEV_PMC_DDR_CNTRL (0x4E4) +#define APBDEV_PMC_SEC_DISABLE (0x004) +#define APBDEV_PMC_SEC_DISABLE2 (0x2C4) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SEC_DISABLE4 (0x5B0) +#define APBDEV_PMC_SEC_DISABLE5 (0x5B4) +#define APBDEV_PMC_SEC_DISABLE6 (0x5B8) +#define APBDEV_PMC_SEC_DISABLE7 (0x5BC) +#define APBDEV_PMC_SEC_DISABLE8 (0x5C0) +#define APBDEV_PMC_SCRATCH43 (0x22C) +#define APBDEV_PMC_SCRATCH190 (0x818) +#define APBDEV_PMC_SCRATCH200 (0x840) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SECURE_SCRATCH4 (0x0C0) +#define APBDEV_PMC_SECURE_SCRATCH5 (0x0C4) +#define APBDEV_PMC_SECURE_SCRATCH6 (0x224) +#define APBDEV_PMC_SECURE_SCRATCH7 (0x228) +#define APBDEV_PMC_SECURE_SCRATCH16 (0x320) +#define APBDEV_PMC_SECURE_SCRATCH21 (0x334) +#define APBDEV_PMC_SECURE_SCRATCH24 (0x340) +#define APBDEV_PMC_SECURE_SCRATCH25 (0x344) +#define APBDEV_PMC_SECURE_SCRATCH26 (0x348) +#define APBDEV_PMC_SECURE_SCRATCH27 (0x34C) +#define APBDEV_PMC_SECURE_SCRATCH32 (0x360) +#define APBDEV_PMC_SECURE_SCRATCH34 (0x368) +#define APBDEV_PMC_SECURE_SCRATCH35 (0x36C) +#define APBDEV_PMC_SECURE_SCRATCH39 (0x37C) +#define APBDEV_PMC_SECURE_SCRATCH51 (0x3AC) +#define APBDEV_PMC_SECURE_SCRATCH55 (0x3BC) +#define APBDEV_PMC_SECURE_SCRATCH74 (0x408) +#define APBDEV_PMC_SECURE_SCRATCH75 (0x40C) +#define APBDEV_PMC_SECURE_SCRATCH76 (0x410) +#define APBDEV_PMC_SECURE_SCRATCH77 (0x414) +#define APBDEV_PMC_SECURE_SCRATCH78 (0x418) +#define APBDEV_PMC_SECURE_SCRATCH99 (0xAE4) +#define APBDEV_PMC_SECURE_SCRATCH100 (0xAE8) +#define APBDEV_PMC_SECURE_SCRATCH101 (0xAEC) +#define APBDEV_PMC_SECURE_SCRATCH102 (0xAF0) +#define APBDEV_PMC_SECURE_SCRATCH103 (0xAF4) +#define APBDEV_PMC_SECURE_SCRATCH112 (0xB18) +#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C) +#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20) +#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24) + + +#define PMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APBDEV_PMC, NAME) +#define PMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (APBDEV_PMC, NAME, VALUE) +#define PMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (APBDEV_PMC, NAME, ENUM) +#define PMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(APBDEV_PMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_PMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (APBDEV_PMC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_PMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_PMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_PMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_PMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_PMC_REG_BIT_ENUM(CNTRL_MAIN_RESET, 4, DISABLE, ENABLE) + +DEFINE_PMC_REG_BIT_ENUM(DPD_SAMPLE_ON, 0, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_ON, 0, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CRAIL, 0, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_MPE, 6, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SAX, 8, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE1, 9, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE2, 10, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE3, 11, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE0, 14, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_C0NC, 15, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SOR, 17, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DIS, 18, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DISB, 19, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBA, 20, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBB, 21, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBC, 22, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VIC, 23, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_IRAM, 24, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVDEC, 25, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVJPG, 26, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_AUD, 27, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DFD, 28, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON); + +DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1); + +DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); + +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CRAIL, 0, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_TE, 1, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VE, 2, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_PCX, 3, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VDE, 4, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_MPE, 6, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_HEG, 7, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SAX, 8, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE1, 9, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE2, 10, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE3, 11, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CELP, 12, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE0, 14, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C0NC, 15, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SOR, 17, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C1NC, 16, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DIS, 18, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DISB, 19, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBA, 20, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBB, 21, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBC, 22, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VIC, 23, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_IRAM, 24, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp new file mode 100644 index 000000000..d6827adeb --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp @@ -0,0 +1,41 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define SB_CSR (0x200) +#define SB_AA64_RESET_LOW (0x230) +#define SB_AA64_RESET_HIGH (0x234) + + +#define SB_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SB, NAME) +#define SB_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SB, NAME, VALUE) +#define SB_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SB, NAME, ENUM) +#define SB_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SB, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_SB_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SB, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_SB_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_SB_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_SB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_SB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_SB_REG_BIT_ENUM(CSR_SECURE_BOOT_FLAG, 0, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_NS_RST_VEC_WR_DIS, 1, ENABLE, DISABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_PIROM_DISABLE, 4, ENABLE, DISABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_HANG, 6, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_SWDM_ENABLE, 7, DISABLE, ENABLE); +DEFINE_SB_REG(CSR_SWDM_FAIL_COUNT, 8, 4); +DEFINE_SB_REG(CSR_COT_FAIL_COUNT, 12, 4); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp new file mode 100644 index 000000000..a191641f9 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp @@ -0,0 +1,36 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define SYSCTR0_CNTFID0 (0x020) +#define SYSCTR0_CNTFID1 (0x024) + + +#define SYSCTR0_COUNTERID4 (0xFD0) +#define SYSCTR0_COUNTERID5 (0xFD4) +#define SYSCTR0_COUNTERID6 (0xFD8) +#define SYSCTR0_COUNTERID7 (0xFDC) +#define SYSCTR0_COUNTERID0 (0xFE0) +#define SYSCTR0_COUNTERID1 (0xFE4) +#define SYSCTR0_COUNTERID2 (0xFE8) +#define SYSCTR0_COUNTERID3 (0xFEC) +#define SYSCTR0_COUNTERID8 (0xFF0) +#define SYSCTR0_COUNTERID9 (0xFF4) +#define SYSCTR0_COUNTERID10 (0xFF8) +#define SYSCTR0_COUNTERID11 (0xFFC) + +#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp new file mode 100644 index 000000000..93a7c8b66 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp @@ -0,0 +1,41 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + + +#define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4) + +#define TIMER_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (TIMER, NAME) +#define TIMER_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (TIMER, NAME, VALUE) +#define TIMER_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (TIMER, NAME, ENUM) +#define TIMER_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(TIMER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_TIMER_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (TIMER, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_TIMER_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_TIMER_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_TIMER_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_TIMER_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, 5, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, 6, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, 7, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, 8, DISABLE, ENABLE); + +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, 12, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, 13, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, 14, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, 15, DISABLE, ENABLE); \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tsec.hpp b/libraries/libexosphere/include/exosphere/tsec.hpp new file mode 100644 index 000000000..454da2efe --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tsec.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::tsec { + + void Lock(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/uart.hpp b/libraries/libexosphere/include/exosphere/uart.hpp new file mode 100644 index 000000000..920f39125 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/uart.hpp @@ -0,0 +1,42 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::uart { + + enum Port { + Port_A = 0, + Port_B = 1, + Port_C = 2, + + Port_Count = 3, + + Port_ReservedDebug = Port_A, + Port_RightJoyCon = Port_B, + Port_LeftJoyCon = Port_C, + }; + + enum Flags { + Flag_None = (0u << 0), + Flag_Inverted = (1u << 0), + }; + + void SetRegisterAddress(uintptr_t address); + + void Initialize(Port port, int baud_rate, u32 flags); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/util.hpp b/libraries/libexosphere/include/exosphere/util.hpp new file mode 100644 index 000000000..088033e7c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/util.hpp @@ -0,0 +1,33 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::util { + + void SetRegisterAddress(uintptr_t address); + + u32 GetMicroSeconds(); + void WaitMicroSeconds(int us); + + void ClearMemory(void *ptr, size_t size); + + template<typename T, typename U> requires std::integral<T> && std::integral<U> + constexpr T DivideUp(T x, U y) { + return (x + (y - 1)) / y; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/wdt.hpp b/libraries/libexosphere/include/exosphere/wdt.hpp new file mode 100644 index 000000000..16a02f263 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/wdt.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::wdt { + + void SetRegisterAddress(uintptr_t address); + void Reboot(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/actmon/actmon_api.cpp b/libraries/libexosphere/source/actmon/actmon_api.cpp new file mode 100644 index 000000000..ee7843cbf --- /dev/null +++ b/libraries/libexosphere/source/actmon/actmon_api.cpp @@ -0,0 +1,42 @@ +/* + * 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> + +namespace ams::actmon { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceActivityMonitor.GetAddress(); + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void HandleInterrupt() { + /* TODO */ + } + + void StartMonitoringBpmp(InterruptHandler handler) { + /* TODO */ + } + + void StopMonitoringBpmp() { + /* TODO */ + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp new file mode 100644 index 000000000..c2de3028e --- /dev/null +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -0,0 +1,98 @@ +/* + * 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 "clkrst_registers.hpp" + +namespace ams::clkrst { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + + struct ClockParameters { + uintptr_t reset_offset; + uintptr_t clk_enb_offset; + uintptr_t clk_src_offset; + u8 index; + u8 clk_src; + u8 clk_div; + }; + + void EnableClock(const ClockParameters ¶m) { + /* Hold reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Disable clock. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + + /* Set the clock source. */ + if (param.clk_src != 0) { + reg::Write(g_register_address + param.clk_src_offset, (param.clk_src << 29) | (param.clk_div << 0)); + } + + /* Enable clk. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Release reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 0)); + } + + // void DisableClock(const ClockParameters ¶m) { + // /* Hold reset. */ + // reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + // + // /* Disable clock. */ + // reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + // } + + #define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \ + constexpr inline const ClockParameters _VARNAME_ = { \ + .reset_offset = CLK_RST_CONTROLLER_RST_DEVICES_##_REG_, \ + .clk_enb_offset = CLK_RST_CONTROLLER_CLK_OUT_ENB_##_REG_, \ + .clk_src_offset = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_, \ + .index = CLK_RST_CONTROLLER_CLK_ENB_##_NAME_##_INDEX, \ + .clk_src = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_##_##_NAME_##_CLK_SRC_##_CLK_, \ + .clk_div = _DIV_, \ + } + + DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void SetFuseVisibility(bool visible) { + reg::ReadWrite(g_register_address + CLK_RST_CONTROLLER_MISC_CLK_ENB, CLK_RST_REG_BITS_VALUE(MISC_CLK_ENB_CFG_ALL_VISIBLE, visible ? 1 : 0)); + } + + void EnableUartAClock() { + EnableClock(UartAClock); + } + + void EnableUartBClock() { + EnableClock(UartAClock); + } + + void EnableUartCClock() { + EnableClock(UartAClock); + } + + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/clkrst/clkrst_registers.hpp b/libraries/libexosphere/source/clkrst/clkrst_registers.hpp new file mode 100644 index 000000000..5d5cd0874 --- /dev/null +++ b/libraries/libexosphere/source/clkrst/clkrst_registers.hpp @@ -0,0 +1,71 @@ +/* + * 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> + +namespace ams::clkrst { + + /* Clock source enums. */ + #define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME) + #define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE) + #define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM) + #define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + + #define CLK_RST_CONTROLLER_RST_SOURCE (0x000) + + #define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) + + DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); + + /* RST_DEVICES */ + #define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004) + #define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008) + #define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C) + #define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C) + #define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4) + #define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358) + #define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C) + + /* CLK_OUT_ENB */ + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) + + /* CLK_SOURCE */ + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) + + /* CLK_ENB_*_INDEX */ + #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) + #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) + #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) + + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + +} diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp new file mode 100644 index 000000000..73f48494d --- /dev/null +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -0,0 +1,121 @@ +/* + * 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 "fuse_registers.hpp" + +namespace ams::fuse { + + namespace { + + struct OdmWord4 { + using HardwareState1 = util::BitPack32::Field<0, 2, int>; + using HardwareType1 = util::BitPack32::Field<HardwareState1::Next, 1, int>; + using DramId = util::BitPack32::Field<HardwareType1::Next, 5, int>; + using HardwareType2 = util::BitPack32::Field<DramId::Next, 1, int>; + using HardwareState2 = util::BitPack32::Field<HardwareType2::Next, 1, int>; + using QuestState = util::BitPack32::Field<HardwareState2::Next, 1, int>; + using FormatVersion = util::BitPack32::Field<QuestState::Next, 1, int>; + using Reserved = util::BitPack32::Field<FormatVersion::Next, 4, int>; + using HardwareType3 = util::BitPack32::Field<Reserved::Next, 4, int>; + }; + + constexpr ALWAYS_INLINE int GetHardwareStateValue(const util::BitPack32 odm_word4) { + constexpr auto HardwareState1Shift = 0; + constexpr auto HardwareState2Shift = OdmWord4::HardwareState1::Count + HardwareState1Shift; + + return (odm_word4.Get<OdmWord4::HardwareState1>() << HardwareState1Shift) | + (odm_word4.Get<OdmWord4::HardwareState2>() << HardwareState2Shift); + } + + constexpr ALWAYS_INLINE int GetHardwareTypeValue(const util::BitPack32 odm_word4) { + constexpr auto HardwareType1Shift = 0; + constexpr auto HardwareType2Shift = OdmWord4::HardwareType1::Count + HardwareType1Shift; + constexpr auto HardwareType3Shift = OdmWord4::HardwareType2::Count + HardwareType2Shift; + + return (odm_word4.Get<OdmWord4::HardwareType1>() << HardwareType1Shift) | + (odm_word4.Get<OdmWord4::HardwareType2>() << HardwareType2Shift) | + (odm_word4.Get<OdmWord4::HardwareType3>() << HardwareType3Shift); + } + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFuses.GetAddress(); + + ALWAYS_INLINE volatile FuseRegisterRegion *GetRegisterRegion() { + return reinterpret_cast<volatile FuseRegisterRegion *>(g_register_address); + } + + ALWAYS_INLINE volatile FuseRegisters &GetRegisters() { + return GetRegisterRegion()->fuse; + } + + ALWAYS_INLINE volatile FuseChipRegisters &GetChipRegisters() { + return GetRegisterRegion()->chip; + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void SetWriteSecureOnly() { + reg::Write(GetRegisters().FUSE_PRIVATEKEYDISABLE, FUSE_REG_BITS_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, KEY_INVISIBLE)); + } + + void Lockout() { + reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE)); + } + + u32 GetOdmWord(int index) { + return GetChipRegisters().FUSE_RESERVED_ODM[index]; + } + + HardwareType GetHardwareType() { + /* Read the odm word. */ + const util::BitPack32 odm_word4 = { GetOdmWord(4) }; + + /* Get the value. */ + const auto value = GetHardwareTypeValue(odm_word4); + + switch (value) { + case 0x01: return HardwareType_Icosa; + case 0x02: return (true /* TODO: GetSocType() == SocType_Mariko */) ? HardwareType_Calcio : HardwareType_Copper; + case 0x04: return HardwareType_Iowa; + case 0x08: return HardwareType_Hoag; + case 0x10: return HardwareType_Five; + default: return HardwareType_Undefined; + } + } + + HardwareState GetHardwareState() { + /* Read the odm word. */ + const util::BitPack32 odm_word4 = { GetOdmWord(4) }; + + /* Get the value. */ + const auto value = GetHardwareStateValue(odm_word4); + + switch (value) { + case 3: return HardwareState_Development; + case 4: return HardwareState_Production; + default: return HardwareState_Undefined; + } + } + + pmic::Regulator GetRegulator() { + /* TODO: How should mariko be handled? This reads from ODM word 28 in fuses (not presesnt in erista...). */ + return pmic::Regulator_Erista_Max77621; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/fuse/fuse_registers.hpp b/libraries/libexosphere/source/fuse/fuse_registers.hpp new file mode 100644 index 000000000..012ef2a87 --- /dev/null +++ b/libraries/libexosphere/source/fuse/fuse_registers.hpp @@ -0,0 +1,221 @@ +/* + * 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> + +namespace ams::fuse { + + struct FuseRegisters { + u32 FUSE_FUSECTRL; + u32 FUSE_FUSEADDR; + u32 FUSE_FUSERDATA; + u32 FUSE_FUSEWDATA; + u32 FUSE_FUSETIME_RD1; + u32 FUSE_FUSETIME_RD2; + u32 FUSE_FUSETIME_PGM1; + u32 FUSE_FUSETIME_PGM2; + u32 FUSE_PRIV2INTFC_START; + u32 FUSE_FUSEBYPASS; + u32 FUSE_PRIVATEKEYDISABLE; + u32 FUSE_DISABLEREGPROGRAM; + u32 FUSE_WRITE_ACCESS_SW; + u32 FUSE_PWR_GOOD_SW; + u32 _0x38; + u32 FUSE_PRIV2RESHIFT; + u32 _0x40[0x3]; + u32 FUSE_FUSETIME_RD3; + u32 _0x50[0xC]; + u32 FUSE_PRIVATE_KEY0_NONZERO; + u32 FUSE_PRIVATE_KEY1_NONZERO; + u32 FUSE_PRIVATE_KEY2_NONZERO; + u32 FUSE_PRIVATE_KEY3_NONZERO; + u32 FUSE_PRIVATE_KEY4_NONZERO; + u32 _0x94[0x1B]; + }; + static_assert(util::is_pod<FuseRegisters>::value); + static_assert(sizeof(FuseRegisters) == 0x100); + + struct FuseChipRegisters { + u32 FUSE_PRODUCTION_MODE; + u32 FUSE_JTAG_SECUREID_VALID; + u32 FUSE_ODM_LOCK; + u32 FUSE_OPT_OPENGL_EN; + u32 FUSE_SKU_INFO; + u32 FUSE_CPU_SPEEDO_0_CALIB; + u32 FUSE_CPU_IDDQ_CALIB; + u32 FUSE_DAC_CRT_CALIB; + u32 FUSE_DAC_HDTV_CALIB; + u32 FUSE_DAC_SDTV_CALIB; + u32 FUSE_OPT_FT_REV; + u32 FUSE_CPU_SPEEDO_1_CALIB; + u32 FUSE_CPU_SPEEDO_2_CALIB; + u32 FUSE_SOC_SPEEDO_0_CALIB; + u32 FUSE_SOC_SPEEDO_1_CALIB; + u32 FUSE_SOC_SPEEDO_2_CALIB; + u32 FUSE_SOC_IDDQ_CALIB; + u32 FUSE_RESERVED_PRODUCTION_WP; + u32 FUSE_FA; + u32 FUSE_RESERVED_PRODUCTION; + u32 FUSE_HDMI_LANE0_CALIB; + u32 FUSE_HDMI_LANE1_CALIB; + u32 FUSE_HDMI_LANE2_CALIB; + u32 FUSE_HDMI_LANE3_CALIB; + u32 FUSE_ENCRYPTION_RATE; + u32 FUSE_PUBLIC_KEY[0x8]; + u32 FUSE_TSENSOR1_CALIB; + u32 FUSE_TSENSOR2_CALIB; + u32 FUSE_VSENSOR_CALIB; + u32 FUSE_OPT_CP_REV; + u32 FUSE_OPT_PFG; + u32 FUSE_TSENSOR0_CALIB; + u32 FUSE_FIRST_BOOTROM_PATCH_SIZE; + u32 FUSE_SECURITY_MODE; + u32 FUSE_PRIVATE_KEY[0x5]; + u32 FUSE_ARM_JTAG_DIS; + u32 FUSE_BOOT_DEVICE_INFO; + u32 FUSE_RESERVED_SW; + u32 FUSE_OPT_VP9_DISABLE; + u32 FUSE_RESERVED_ODM[0x8]; + u32 FUSE_OBS_DIS; + u32 FUSE_NOR_INFO; + u32 FUSE_USB_CALIB; + u32 FUSE_SKU_DIRECT_CONFIG; + u32 FUSE_KFUSE_PRIVKEY_CTRL; + u32 FUSE_PACKAGE_INFO; + u32 FUSE_OPT_VENDOR_CODE; + u32 FUSE_OPT_FAB_CODE; + u32 FUSE_OPT_LOT_CODE_0; + u32 FUSE_OPT_LOT_CODE_1; + u32 FUSE_OPT_WAFER_ID; + u32 FUSE_OPT_X_COORDINATE; + u32 FUSE_OPT_Y_COORDINATE; + u32 FUSE_OPT_SEC_DEBUG_EN; + u32 FUSE_OPT_OPS_RESERVED; + u32 FUSE_SATA_CALIB; + u32 FUSE_GPU_IDDQ_CALIB; + u32 FUSE_TSENSOR3_CALIB; + u32 FUSE_SKU_BOND_OUT_L; + u32 FUSE_SKU_BOND_OUT_H; + u32 FUSE_SKU_BOND_OUT_U; + u32 FUSE_SKU_BOND_OUT_V; + u32 FUSE_SKU_BOND_OUT_W; + u32 FUSE_OPT_SAMPLE_TYPE; + u32 FUSE_OPT_SUBREVISION; + u32 FUSE_OPT_SW_RESERVED_0; + u32 FUSE_OPT_SW_RESERVED_1; + u32 FUSE_TSENSOR4_CALIB; + u32 FUSE_TSENSOR5_CALIB; + u32 FUSE_TSENSOR6_CALIB; + u32 FUSE_TSENSOR7_CALIB; + u32 FUSE_OPT_PRIV_SEC_EN; + u32 FUSE_PKC_DISABLE; + u32 _0x16C; + u32 _0x170; + u32 _0x174; + u32 _0x178; + u32 FUSE_FUSE2TSEC_DEBUG_DISABLE; + u32 FUSE_TSENSOR_COMMON; + u32 FUSE_OPT_CP_BIN; + u32 FUSE_OPT_GPU_DISABLE; + u32 FUSE_OPT_FT_BIN; + u32 FUSE_OPT_DONE_MAP; + u32 _0x194; + u32 FUSE_APB2JTAG_DISABLE; + u32 FUSE_ODM_INFO; + u32 _0x1A0; + u32 _0x1A4; + u32 FUSE_ARM_CRYPT_DE_FEATURE; + u32 _0x1AC; + u32 _0x1B0; + u32 _0x1B4; + u32 _0x1B8; + u32 _0x1BC; + u32 FUSE_WOA_SKU_FLAG; + u32 FUSE_ECO_RESERVE_1; + u32 FUSE_GCPLEX_CONFIG_FUSE; + u32 FUSE_PRODUCTION_MONTH; + u32 FUSE_RAM_REPAIR_INDICATOR; + u32 FUSE_TSENSOR9_CALIB; + u32 _0x1D8; + u32 FUSE_VMIN_CALIBRATION; + u32 FUSE_AGING_SENSOR_CALIBRATION; + u32 FUSE_DEBUG_AUTHENTICATION; + u32 FUSE_SECURE_PROVISION_INDEX; + u32 FUSE_SECURE_PROVISION_INFO; + u32 FUSE_OPT_GPU_DISABLE_CP1; + u32 FUSE_SPARE_ENDIS; + u32 FUSE_ECO_RESERVE_0; + u32 _0x1FC; + u32 _0x200; + u32 FUSE_RESERVED_CALIB0; + u32 FUSE_RESERVED_CALIB1; + u32 FUSE_OPT_GPU_TPC0_DISABLE; + u32 FUSE_OPT_GPU_TPC0_DISABLE_CP1; + u32 FUSE_OPT_CPU_DISABLE; + u32 FUSE_OPT_CPU_DISABLE_CP1; + u32 FUSE_TSENSOR10_CALIB; + u32 FUSE_TSENSOR10_CALIB_AUX; + u32 FUSE_OPT_RAM_SVOP_DP; + u32 FUSE_OPT_RAM_SVOP_PDP; + u32 FUSE_OPT_RAM_SVOP_REG; + u32 FUSE_OPT_RAM_SVOP_SP; + u32 FUSE_OPT_RAM_SVOP_SMPDP; + u32 FUSE_OPT_GPU_TPC0_DISABLE_CP2; + u32 FUSE_OPT_GPU_TPC1_DISABLE; + u32 FUSE_OPT_GPU_TPC1_DISABLE_CP1; + u32 FUSE_OPT_GPU_TPC1_DISABLE_CP2; + u32 FUSE_OPT_CPU_DISABLE_CP2; + u32 FUSE_OPT_GPU_DISABLE_CP2; + u32 FUSE_USB_CALIB_EXT; + u32 FUSE_RESERVED_FIELD; + u32 FUSE_OPT_ECC_EN; + u32 _0x25C; + u32 _0x260; + u32 _0x264; + u32 _0x268; + u32 _0x26C; + u32 _0x270; + u32 _0x274; + u32 _0x278; + u32 FUSE_SPARE_REALIGNMENT_REG; + u32 FUSE_SPARE_BIT[0x20]; + }; + static_assert(util::is_pod<FuseChipRegisters>::value); + static_assert(sizeof(FuseChipRegisters) == 0x300); + + struct FuseRegisterRegion { + FuseRegisters fuse; + FuseChipRegisters chip; + }; + static_assert(util::is_pod<FuseRegisterRegion>::value); + static_assert(sizeof(FuseRegisterRegion) == secmon::MemoryRegionPhysicalDeviceFuses.GetSize()); + + #define FUSE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FUSE, NAME) + #define FUSE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FUSE, NAME, VALUE) + #define FUSE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FUSE, NAME, ENUM) + #define FUSE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FUSE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_FUSE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FUSE, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_FUSE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_FUSE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_FUSE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_FUSE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, 4, KEY_VISIBLE, KEY_INVISIBLE); + DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_PRIVATEKEYDISABLE_VAL_KEY, 0, VISIBLE, INVISIBLE); + + DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE); + +} diff --git a/libraries/libexosphere/source/gic/gic_api.cpp b/libraries/libexosphere/source/gic/gic_api.cpp new file mode 100644 index 000000000..70e36002c --- /dev/null +++ b/libraries/libexosphere/source/gic/gic_api.cpp @@ -0,0 +1,217 @@ +/* + * 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> + +namespace ams::gic { + + namespace { + + struct GicDistributor { + u32 ctlr; + u32 typer; + u32 iidr; + u32 reserved_0x0c; + u32 statusr; + u32 reserved_0x14[3]; + u32 impldef_0x20[8]; + u32 setspi_nsr; + u32 reserved_0x44; + u32 clrspi_nsr; + u32 reserved_0x4c; + u32 setspi_sr; + u32 reserved_0x54; + u32 clrspi_sr; + u32 reserved_0x5c[9]; + u32 igroupr[32]; + u32 isenabler[32]; + u32 icenabler[32]; + u32 ispendr[32]; + u32 icpendr[32]; + u32 isactiver[32]; + u32 icactiver[32]; + union { + u8 bytes[1020]; + u32 words[255]; + } ipriorityr; + u32 _0x7fc; + union { + u8 bytes[1020]; + u32 words[255]; + } itargetsr; + u32 _0xbfc; + u32 icfgr[64]; + u32 igrpmodr[32]; + u32 _0xd80[32]; + u32 nsacr[64]; + u32 sgir; + u32 _0xf04[3]; + u32 cpendsgir[4]; + u32 spendsgir[4]; + u32 reserved_0xf30[52]; + + static constexpr size_t SgirCpuTargetListShift = 16; + + enum SgirTargetListFilter : u32 { + SgirTargetListFilter_CpuTargetList = (0 << 24), + SgirTargetListFilter_Others = (1 << 24), + SgirTargetListFilter_Self = (2 << 24), + SgirTargetListFilter_Reserved = (3 << 24), + }; + }; + static_assert(util::is_pod<GicDistributor>::value); + static_assert(sizeof(GicDistributor) == 0x1000); + static_assert(sizeof(GicDistributor) == secmon::MemoryRegionPhysicalDeviceGicDistributor.GetSize()); + + struct GicCpuInterface { + u32 ctlr; + u32 pmr; + u32 bpr; + u32 iar; + u32 eoir; + u32 rpr; + u32 hppir; + u32 abpr; + u32 aiar; + u32 aeoir; + u32 ahppir; + u32 statusr; + u32 reserved_30[4]; + u32 impldef_40[36]; + u32 apr[4]; + u32 nsapr[4]; + u32 reserved_f0[3]; + u32 iidr; + u32 reserved_100[960]; + u32 dir; + u32 _0x1004[1023]; + }; + static_assert(util::is_pod<GicCpuInterface>::value); + static_assert(sizeof(GicCpuInterface) == 0x2000); + static_assert(sizeof(GicCpuInterface) == secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetSize()); + + constexpr inline int InterruptWords = InterruptCount / BITSIZEOF(u32); + constexpr inline int SpiIndex = BITSIZEOF(u32); + + constinit uintptr_t g_distributor_address = secmon::MemoryRegionPhysicalDeviceGicDistributor.GetAddress(); + constinit uintptr_t g_cpu_interface_address = secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetAddress(); + + volatile GicDistributor *GetDistributor() { + return reinterpret_cast<volatile GicDistributor *>(g_distributor_address); + } + + volatile GicCpuInterface *GetCpuInterface() { + return reinterpret_cast<volatile GicCpuInterface *>(g_cpu_interface_address); + } + + void ReadWrite(uintptr_t address, int width, int i, u32 value) { + /* This code will never be invoked with a negative interrupt id. */ + AMS_ASSUME(i >= 0); + + const int scale = BITSIZEOF(u32) / width; + const int word = i / scale; + const int bit = (i % scale) * width; + + reg::ReadWrite(address + sizeof(u32) * word, REG_BITS_VALUE(bit, width, value)); + } + + void Write(uintptr_t address, int width, int i, u32 value) { + /* This code will never be invoked with a negative interrupt id. */ + AMS_ASSUME(i >= 0); + + const int scale = BITSIZEOF(u32) / width; + const int word = i / scale; + const int bit = (i % scale) * width; + + reg::Write(address + sizeof(u32) * word, value << bit); + } + + } + + void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address) { + g_distributor_address = distributor_address; + g_cpu_interface_address = cpu_interface_address; + } + + void InitializeCommon() { + /* Get the gicd registers. */ + auto *gicd = GetDistributor(); + + /* Set IGROUPR for to be FFs. */ + for (int i = SpiIndex / BITSIZEOF(u32); i < InterruptWords; ++i) { + gicd->igroupr[i] = 0xFFFFFFFFu; + } + + /* Set IPRIORITYR for spi interrupts to be 0x80. */ + for (int i = SpiIndex; i < InterruptCount; ++i) { + gicd->ipriorityr.bytes[i] = 0x80; + } + + /* Enable group 0. */ + gicd->ctlr = 1; + } + + void InitializeCoreUnique() { + /* Get the registers. */ + auto *gicd = GetDistributor(); + auto *gicc = GetCpuInterface(); + + /* Set IGROUPR0 to be FFs. */ + gicd->igroupr[0] = 0xFFFFFFFFu; + + /* Set IPRIORITYR for core local interrupts to be 0x80. */ + for (int i = 0; i < SpiIndex; ++i) { + gicd->ipriorityr.bytes[i] = 0x80; + } + + /* Enable group 0 as FIQs. */ + gicc->ctlr = 0x1D9; + + /* Set PMR. */ + gicc->pmr = 0x80; + + /* Set BPR. */ + gicc->bpr = 7; + } + + void SetPriority(int interrupt_id, int priority) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, ipriorityr), BITSIZEOF(u8), interrupt_id, priority); + } + + void SetInterruptGroup(int interrupt_id, int group) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, igroupr), 1, interrupt_id, group); + } + + void SetEnable(int interrupt_id, bool enable) { + Write(g_distributor_address + offsetof(GicDistributor, isenabler), 1, interrupt_id, enable); + } + + void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, itargetsr), BITSIZEOF(u8), interrupt_id, cpu_mask); + } + + void SetSpiMode(int interrupt_id, InterruptMode mode) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast<u32>(mode) << 1); + } + + int GetInterruptRequestId() { + return reg::Read(GetCpuInterface()->iar); + } + + void SetEndOfInterrupt(int interrupt_id) { + reg::Write(GetCpuInterface()->eoir, interrupt_id); + } + +} diff --git a/libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp b/libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp new file mode 100644 index 000000000..be19c1183 --- /dev/null +++ b/libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp @@ -0,0 +1,29 @@ +/* + * 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> + +namespace ams::hw::arch::arm64 { + + void FlushDataCache(const void *ptr, size_t size) { + const uintptr_t start = reinterpret_cast<uintptr_t>(ptr); + const uintptr_t end = util::AlignUp(start + size, hw::DataCacheLineSize); + + for (uintptr_t cur = start; cur < end; cur += hw::DataCacheLineSize) { + FlushDataCacheLine(reinterpret_cast<void *>(cur)); + } + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/i2c/i2c_api.cpp b/libraries/libexosphere/source/i2c/i2c_api.cpp new file mode 100644 index 000000000..108429768 --- /dev/null +++ b/libraries/libexosphere/source/i2c/i2c_api.cpp @@ -0,0 +1,202 @@ +/* + * 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 "i2c_registers.hpp" + +namespace ams::i2c { + + namespace { + + constexpr inline size_t MaxTransferSize = sizeof(u32); + + constinit std::array<uintptr_t, Port_Count> g_register_addresses = [] { + std::array<uintptr_t, Port_Count> arr = {}; + + arr[Port_1] = secmon::MemoryRegionPhysicalDeviceI2c1.GetAddress(); + arr[Port_5] = secmon::MemoryRegionPhysicalDeviceI2c5.GetAddress(); + + return arr; + }(); + + void LoadConfig(uintptr_t address) { + /* Configure for TIMEOUT and MSTR config load. */ + /* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */ + /* We will reproduce the write just in case it is undocumented. */ + reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1), + I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Wait up to 20 microseconds for the master config to be loaded. */ + for (int i = 0; i < 20; ++i) { + if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) { + return; + } + util::WaitMicroSeconds(1); + } + } + + void ClearBus(uintptr_t address) { + /* Configure the bus clear register. */ + reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + /* Load the config. */ + LoadConfig(address); + + /* Wait up to 250us (in 25 us increments) until the bus clear is done. */ + for (int i = 0; i < 10; ++i) { + if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) { + break; + } + + util::WaitMicroSeconds(25); + } + + /* Read the bus clear status. */ + reg::Read(address + I2C_BUS_CLEAR_STATUS); + } + + void InitializePort(uintptr_t address) { + /* Calculate the divisor. */ + constexpr int Divisor = util::DivideUp(19200, 8 * 400); + + /* Set the divisor. */ + reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1), + I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1)); + + /* Clear the bus. */ + ClearBus(address); + + /* Clear the status. */ + reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER)); + } + + bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { + /* Ensure we don't write too much. */ + u32 data = 0; + if (src_size > MaxTransferSize) { + return false; + } + + /* Copy the data to a transfer word. */ + std::memcpy(std::addressof(data), src, src_size); + + + /* Configure the to write the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE)); + + /* Configure to write the data. */ + reg::Write(base_address + I2C_I2C_CMD_DATA1, data); + + /* Configure to write the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check if the transfer was successful. */ + return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL)); + } + + bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { + /* Ensure we don't read too much. */ + if (dst_size > MaxTransferSize) { + return false; + } + + /* Configure the to read the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ)); + + /* Configure to read the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check that the transfer was successful. */ + if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) { + return false; + } + + /* Read and copy out the data. */ + u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1); + std::memcpy(dst, std::addressof(data), dst_size); + return true; + } + + } + + void SetRegisterAddress(Port port, uintptr_t address) { + g_register_addresses[port] = address; + } + + void Initialize(Port port) { + InitializePort(g_register_addresses[port]); + } + + bool Query(void *dst, size_t dst_size, Port port, int address, int r) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Select the register we want to read. */ + bool success = Write(base_address, port, address, std::addressof(r), 1, false); + if (success) { + /* If we successfully selected, read data from the register. */ + success = Read(base_address, port, dst, dst_size, address, true); + } + + return success; + } + + bool Send(Port port, int address, int r, const void *src, size_t src_size) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Create a transfer buffer, make sure we can use it. */ + u8 buffer[MaxTransferSize]; + if (src_size > sizeof(buffer) - 1) { + return false; + } + + /* Copy data into the buffer. */ + buffer[0] = static_cast<u8>(r); + std::memcpy(buffer + 1, src, src_size); + + return Write(base_address, port, address, buffer, src_size + 1, false); + } + +} diff --git a/libraries/libexosphere/source/i2c/i2c_registers.hpp b/libraries/libexosphere/source/i2c/i2c_registers.hpp new file mode 100644 index 000000000..48f659574 --- /dev/null +++ b/libraries/libexosphere/source/i2c/i2c_registers.hpp @@ -0,0 +1,77 @@ +/* + * 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> + +namespace ams::i2c { + + #define I2C_I2C_CNFG (0x000) + #define I2C_I2C_CMD_ADDR0 (0x004) + #define I2C_I2C_CMD_DATA1 (0x00C) + #define I2C_I2C_STATUS (0x01C) + #define I2C_INTERRUPT_STATUS_REGISTER (0x068) + #define I2C_CLK_DIVISOR_REGISTER (0x06C) + #define I2C_BUS_CLEAR_CONFIG (0x084) + #define I2C_BUS_CLEAR_STATUS (0x088) + #define I2C_CONFIG_LOAD (0x08C) + + #define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME) + #define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE) + #define I2C_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (I2C, NAME, ENUM) + #define I2C_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(I2C, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_I2C_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (I2C, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_I2C_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_I2C_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_I2C_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_I2C_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + /* I2C_CNFG */ + DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE); + DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T); + + /* I2C_CMD_ADDR0 */ + DEFINE_I2C_REG_BIT_ENUM(I2C_CMD_ADDR0_7BIT_RW, 0, WRITE, READ); + DEFINE_I2C_REG(I2C_CMD_ADDR0_7BIT_ADDR, 1, 7); + + /* I2C_STATUS */ + DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_NOACK_FOR_BYTE1, SL1_NOACK_FOR_BYTE2, SL1_NOACK_FOR_BYTE3, SL1_NOACK_FOR_BYTE4, SL1_NOACK_FOR_BYTE5, SL1_NOACK_FOR_BYTE6, SL1_NOACK_FOR_BYTE7, SL1_NOACK_FOR_BYTE8, SL1_NOACK_FOR_BYTE9, SL1_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY); + + /* INTERRUPT_STATUS_REGISTER */ + DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET); + + /* CLK_DIVISOR_REGISTER */ + DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16); + DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_STD_FAST_MODE, 16, 16); + + /* BUS_CLEAR_CONFIG */ + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, 0, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE); + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP); + DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8); + + /* CONFIG_LOAD */ + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE); + DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1); + + +} diff --git a/libraries/libexosphere/source/libc/libc.c b/libraries/libexosphere/source/libc/libc.c new file mode 100644 index 000000000..2117f2483 --- /dev/null +++ b/libraries/libexosphere/source/libc/libc.c @@ -0,0 +1,1141 @@ +/* Note: copied from newlib */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <string.h> +#include <stddef.h> +#include <limits.h> + +/* + * Copyright (C) 2004 CodeSourcery, LLC + * + * Permission to use, copy, modify, and distribute this file + * for any purpose is hereby granted without fee, provided that + * the above copyright notice and this notice appears in all + * copies. + * + * This file is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* Handle ELF .{pre_init,init,fini}_array sections. */ +#include <sys/types.h> + +#ifndef HAVE_INITFINI_ARRAY +#define HAVE_INITFINI_ARRAY +#endif + +#undef HAVE_INIT_FINI + +#ifdef HAVE_INITFINI_ARRAY + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start []) (void) __attribute__((weak)); +extern void (*__preinit_array_end []) (void) __attribute__((weak)); +extern void (*__init_array_start []) (void) __attribute__((weak)); +extern void (*__init_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _init (void); +#endif + +/* Iterate over all the init routines. */ +void +__libc_init_array (void) +{ + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) + __preinit_array_start[i] (); + +#ifdef HAVE_INIT_FINI + _init (); +#endif + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) + __init_array_start[i] (); +} +#endif + +#ifdef HAVE_INITFINI_ARRAY +extern void (*__fini_array_start []) (void) __attribute__((weak)); +extern void (*__fini_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _fini (void); +#endif + +/* Run all the cleanup routines. */ +void +__libc_fini_array (void) +{ + size_t count; + size_t i; + + count = __fini_array_end - __fini_array_start; + for (i = count; i > 0; i--) + __fini_array_start[i-1] (); + +#ifdef HAVE_INIT_FINI + _fini (); +#endif +} +#endif + +/* +FUNCTION + <<memmove>>---move possibly overlapping memory +INDEX + memmove +SYNOPSIS + #include <string.h> + void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>); +DESCRIPTION + This function moves <[length]> characters from the block of + memory starting at <<*<[src]>>> to the memory starting at + <<*<[dst]>>>. <<memmove>> reproduces the characters correctly + at <<*<[dst]>>> even if the two areas overlap. +RETURNS + The function returns <[dst]> as passed. +PORTABILITY +<<memmove>> is ANSI C. +<<memmove>> requires no supporting OS subroutines. +QUICKREF + memmove ansi pure +*/ + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#undef TOO_SMALL +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +/*SUPPRESS 20*/ +void * +//__inhibit_loop_to_libcall +memmove (void *dst_void, + const void *src_void, + size_t length) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = dst_void; + const char *src = src_void; + + if (src < dst && dst < src + length) + { + /* Have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#else + char *dst = dst_void; + const char *src = src_void; + long *aligned_dst; + const long *aligned_src; + + if (src < dst && dst < src + length) + { + /* Destructive overlap...have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + /* Use optimizing algorithm for a non-destructive copy to closely + match memcpy. If the size is small or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (length >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + length -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (length >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + length -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<memcpy>>---copy memory regions +SYNOPSIS + #include <string.h> + void* memcpy(void *restrict <[out]>, const void *restrict <[in]>, + size_t <[n]>); +DESCRIPTION + This function copies <[n]> bytes from the memory region + pointed to by <[in]> to the memory region pointed to by + <[out]>. + If the regions overlap, the behavior is undefined. +RETURNS + <<memcpy>> returns a pointer to the first byte of the <[out]> + region. +PORTABILITY +<<memcpy>> is ANSI C. +<<memcpy>> requires no supporting OS subroutines. +QUICKREF + memcpy ansi pure + */ + +void * +memcpy (void * dst0, + const void * __restrict src0, + size_t len0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = (char *) dst0; + char *src = (char *) src0; + + void *save = dst0; + + while (len0--) + { + *dst++ = *src++; + } + + return save; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (len0 >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len0 -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len0 >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len0 -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (len0--) + *dst++ = *src++; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<memset>>---set an area of memory +INDEX + memset +SYNOPSIS + #include <string.h> + void *memset(void *<[dst]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function converts the argument <[c]> into an unsigned + char and fills the first <[length]> characters of the array + pointed to by <[dst]> to the value. +RETURNS + <<memset>> returns the value of <[dst]>. +PORTABILITY +<<memset>> is ANSI C. + <<memset>> requires no supporting OS subroutines. +QUICKREF + memset ansi pure +*/ + +#include <string.h> + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof(long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +void * +memset (void *m, + int c, + size_t n) +{ + char *s = (char *) m; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned int i; + unsigned long buffer; + unsigned long *aligned_addr; + unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an + unsigned variable. */ + + while (UNALIGNED (s)) + { + if (n--) + *s++ = (char) c; + else + return m; + } + + if (!TOO_SMALL (n)) + { + /* If we get this far, we know that n is large and s is word-aligned. */ + aligned_addr = (unsigned long *) s; + + /* Store D into each char sized location in BUFFER so that + we can set large blocks quickly. */ + buffer = (d << 8) | d; + buffer |= (buffer << 16); + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + buffer = (buffer << i) | buffer; + + /* Unroll the loop. */ + while (n >= LBLOCKSIZE*4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + n -= 4*LBLOCKSIZE; + } + + while (n >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + n -= LBLOCKSIZE; + } + /* Pick up the remainder with a bytewise loop. */ + s = (char*)aligned_addr; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (n--) + *s++ = (char) c; + + return m; +} + +/* +FUNCTION + <<memchr>>---find character in memory +INDEX + memchr +SYNOPSIS + #include <string.h> + void *memchr(const void *<[src]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function searches memory starting at <<*<[src]>>> for the + character <[c]>. The search only ends with the first + occurrence of <[c]>, or after <[length]> characters; in + particular, <<NUL>> does not terminate the search. +RETURNS + If the character <[c]> is found within <[length]> characters + of <<*<[src]>>>, a pointer to the character is returned. If + <[c]> is not found, then <<NULL>> is returned. +PORTABILITY +<<memchr>> is ANSI C. +<<memchr>> requires no supporting OS subroutines. +QUICKREF + memchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the bytewise iterator. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +#if LONG_MAX == 2147483647L +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) +#else +#if LONG_MAX == 9223372036854775807L +/* Nonzero if X (a long int) contains a NULL byte. */ +#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) +#else +#error long int is not a 32bit or 64bit type. +#endif +#endif + +#ifndef DETECTNULL +#error long int is not a 32bit or 64bit byte +#endif + +/* DETECTCHAR returns nonzero if (long)X contains the byte used + to fill (long)MASK. */ +#define DETECTCHAR(X,MASK) (DETECTNULL(X ^ MASK)) + +void * +memchr (const void *src_void, + int c, + size_t length) +{ + const unsigned char *src = (const unsigned char *) src_void; + unsigned char d = c; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *asrc; + unsigned long mask; + unsigned int i; + + while (UNALIGNED (src)) + { + if (!length--) + return NULL; + if (*src == d) + return (void *) src; + src++; + } + + if (!TOO_SMALL (length)) + { + /* If we get this far, we know that length is large and src is + word-aligned. */ + /* The fast code reads the source one word at a time and only + performs the bytewise search on word-sized segments if they + contain the search character, which is detected by XORing + the word-sized segment with a word-sized block of the search + character and then detecting for the presence of NUL in the + result. */ + asrc = (unsigned long *) src; + mask = d << 8 | d; + mask = mask << 16 | mask; + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + mask = (mask << i) | mask; + + while (length >= LBLOCKSIZE) + { + if (DETECTCHAR (*asrc, mask)) + break; + length -= LBLOCKSIZE; + asrc++; + } + + /* If there are fewer than LBLOCKSIZE characters left, + then we resort to the bytewise loop. */ + + src = (unsigned char *) asrc; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (length--) + { + if (*src == d) + return (void *) src; + src++; + } + + return NULL; +} + +/* +FUNCTION + <<memcmp>>---compare two memory areas +INDEX + memcmp +SYNOPSIS + #include <string.h> + int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>); +DESCRIPTION + This function compares not more than <[n]> characters of the + object pointed to by <[s1]> with the object pointed to by <[s2]>. +RETURNS + The function returns an integer greater than, equal to or + less than zero according to whether the object pointed to by + <[s1]> is greater than, equal to or less than the object + pointed to by <[s2]>. +PORTABILITY +<<memcmp>> is ANSI C. +<<memcmp>> requires no supporting OS subroutines. +QUICKREF + memcmp ansi pure +*/ + + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +int +memcmp (const void *m1, + const void *m2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + + while (n--) + { + if (*s1 != *s2) + { + return *s1 - *s2; + } + s1++; + s2++; + } + return 0; +#else + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + unsigned long *a1; + unsigned long *a2; + + /* If the size is too small, or either pointer is unaligned, + then we punt to the byte compare loop. Hopefully this will + not turn up in inner loops. */ + if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) + { + /* Otherwise, load and compare the blocks of memory one + word at a time. */ + a1 = (unsigned long*) s1; + a2 = (unsigned long*) s2; + while (n >= LBLOCKSIZE) + { + if (*a1 != *a2) + break; + a1++; + a2++; + n -= LBLOCKSIZE; + } + + /* check m mod LBLOCKSIZE remaining characters */ + + s1 = (unsigned char*)a1; + s2 = (unsigned char*)a2; + } + + while (n--) + { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + + return 0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<strchr>>---search for character in string +INDEX + strchr +SYNOPSIS + #include <string.h> + char * strchr(const char *<[string]>, int <[c]>); +DESCRIPTION + This function finds the first occurence of <[c]> (converted to + a char) in the string pointed to by <[string]> (including the + terminating null character). +RETURNS + Returns a pointer to the located character, or a null pointer + if <[c]> does not occur in <[string]>. +PORTABILITY +<<strchr>> is ANSI C. +<<strchr>> requires no supporting OS subroutines. +QUICKREF + strchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if X is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +char * +strchr (const char *s1, + int i) +{ + const unsigned char *s = (const unsigned char *)s1; + unsigned char c = i; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long mask,j; + unsigned long *aligned_addr; + + /* Special case for finding 0. */ + if (!c) + { + while (UNALIGNED (s)) + { + if (!*s) + return (char *) s; + s++; + } + /* Operate a word at a time. */ + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + /* Found the end of string. */ + s = (const unsigned char *) aligned_addr; + while (*s) + s++; + return (char *) s; + } + + /* All other bytes. Align the pointer, then search a long at a time. */ + while (UNALIGNED (s)) + { + if (!*s) + return NULL; + if (*s == c) + return (char *) s; + s++; + } + + mask = c; + for (j = 8; j < LBLOCKSIZE * 8; j <<= 1) + mask = (mask << j) | mask; + + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr) && !DETECTCHAR (*aligned_addr, mask)) + aligned_addr++; + + /* The block of bytes currently pointed to by aligned_addr + contains either a null or the target char, or both. We + catch it using the bytewise search. */ + + s = (unsigned char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*s && *s != c) + s++; + if (*s == c) + return (char *)s; + return NULL; +} + +/* +FUNCTION + <<strcmp>>---character string compare + +INDEX + strcmp +SYNOPSIS + #include <string.h> + int strcmp(const char *<[a]>, const char *<[b]>); +DESCRIPTION + <<strcmp>> compares the string at <[a]> to + the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <<strcmp>> returns a number greater than zero. If the two + strings match, <<strcmp>> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <<strcmp>> returns a + number less than zero. +PORTABILITY +<<strcmp>> is ANSI C. +<<strcmp>> requires no supporting OS subroutines. +QUICKREF + strcmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strcmp (const char *s1, + const char *s2) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (*a1 == *a2) + { + /* To get here, *a1 == *a2, thus if we find a null in *a1, + then the strings must be equal, so return zero. */ + if (DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<strcpy>>---copy string +INDEX + strcpy +SYNOPSIS + #include <string.h> + char *strcpy(char *<[dst]>, const char *<[src]>); +DESCRIPTION + <<strcpy>> copies the string pointed to by <[src]> + (including the terminating null character) to the array + pointed to by <[dst]>. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<<strcpy>> is ANSI C. +<<strcpy>> requires no supporting OS subroutines. +QUICKREF + strcpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +char* +strcpy (char *dst0, + const char *src0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *s = dst0; + + while ((*dst0++ = *src0++)) + ; + + return s; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC or DEST is unaligned, then copy bytes. */ + if (!UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (!DETECTNULL(*aligned_src)) + { + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while ((*dst++ = *src++)) + ; + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<strlen>>---character string length +INDEX + strlen +SYNOPSIS + #include <string.h> + size_t strlen(const char *<[str]>); +DESCRIPTION + The <<strlen>> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a <<NULL>> character. +RETURNS + <<strlen>> returns the character count. +PORTABILITY +<<strlen>> is ANSI C. +<<strlen>> requires no supporting OS subroutines. +QUICKREF + strlen ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof (long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +size_t +strlen (const char *str) +{ + const char *start = str; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *aligned_addr; + + /* Align the pointer, so we can search a word at a time. */ + while (UNALIGNED (str)) + { + if (!*str) + return str - start; + str++; + } + + /* If the string is word-aligned, we can check for the presence of + a null in each word-sized block. */ + aligned_addr = (unsigned long *)str; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + + /* Once a null is detected, we check each byte in that block for a + precise position of the null. */ + str = (char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*str) + str++; + return str - start; +} + +/* +FUNCTION + <<strncmp>>---character string compare + +INDEX + strncmp +SYNOPSIS + #include <string.h> + int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>); +DESCRIPTION + <<strncmp>> compares up to <[length]> characters + from the string at <[a]> to the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <<strncmp>> returns a number greater than zero. If the two + strings are equivalent, <<strncmp>> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <<strncmp>> returns a + number less than zero. +PORTABILITY +<<strncmp>> is ANSI C. +<<strncmp>> requires no supporting OS subroutines. +QUICKREF + strncmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strncmp (const char *s1, + const char *s2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + if (n == 0) + return 0; + + while (n-- != 0 && *s1 == *s2) + { + if (n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + if (n == 0) + return 0; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (n >= sizeof (long) && *a1 == *a2) + { + n -= sizeof (long); + + /* If we've run out of bytes or hit a null, return zero + since we already know *a1 == *a2. */ + if (n == 0 || DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (n-- > 0 && *s1 == *s2) + { + /* If we've run out of bytes or hit a null, return zero + since we already know *s1 == *s2. */ + if (n == 0 || *s1 == '\0') + return 0; + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<strncpy>>---counted copy string +INDEX + strncpy +SYNOPSIS + #include <string.h> + char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>, + size_t <[length]>); +DESCRIPTION + <<strncpy>> copies not more than <[length]> characters from the + the string pointed to by <[src]> (including the terminating + null character) to the array pointed to by <[dst]>. If the + string pointed to by <[src]> is shorter than <[length]> + characters, null characters are appended to the destination + array until a total of <[length]> characters have been + written. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<<strncpy>> is ANSI C. +<<strncpy>> requires no supporting OS subroutines. +QUICKREF + strncpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +#define TOO_SMALL(LEN) ((LEN) < sizeof (long)) + +char * +strncpy (char *__restrict dst0, + const char *__restrict src0, + size_t count) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dscan; + const char *sscan; + + dscan = dst0; + sscan = src0; + while (count > 0) + { + --count; + if ((*dscan++ = *sscan++) == '\0') + break; + } + while (count-- > 0) + *dscan++ = '\0'; + + return dst0; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC and DEST is aligned and count large enough, then copy words. */ + if (!UNALIGNED (src, dst) && !TOO_SMALL (count)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (count >= sizeof (long int) && !DETECTNULL(*aligned_src)) + { + count -= sizeof (long int); + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (count > 0) + { + --count; + if ((*dst++ = *src++) == '\0') + break; + } + + while (count-- > 0) + *dst++ = '\0'; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <<strnlen>>---character string length + +INDEX + strnlen +SYNOPSIS + #include <string.h> + size_t strnlen(const char *<[str]>, size_t <[n]>); +DESCRIPTION + The <<strnlen>> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a NUL character or the maximum: <[n]> number of + characters have been inspected. +RETURNS + <<strnlen>> returns the character count or <[n]>. +PORTABILITY +<<strnlen>> is a GNU extension. +<<strnlen>> requires no supporting OS subroutines. +*/ + +size_t +strnlen (const char *str, + size_t n) +{ + const char *start = str; + + while (n-- > 0 && *str) + str++; + + return str - start; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp new file mode 100644 index 000000000..0cf20e207 --- /dev/null +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -0,0 +1,53 @@ +/* + * 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> + +namespace ams::log { + + namespace { + + constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug; + constinit bool g_initialized_uart = false; + + constexpr inline u32 UartPortFlags = [] { + if constexpr (UartLogPort == uart::Port_ReservedDebug) { + /* Logging to the debug port. */ + /* Don't invert transactions. */ + return uart::Flag_None; + } else if constexpr (UartLogPort == uart::Port_LeftJoyCon) { + /* Logging to left joy-con (e.g. with Joyless). */ + /* Invert transactions. */ + return uart::Flag_Inverted; + } else if constexpr (UartLogPort == uart::Port_RightJoyCon) { + /* Logging to right joy-con (e.g. with Joyless). */ + /* Invert transactions. */ + return uart::Flag_Inverted; + } else { + __builtin_unreachable(); + } + }(); + + } + + void Initialize() { + /* Initialize the target uart port. */ + uart::Initialize(UartLogPort, 115200, UartPortFlags); + + /* Note that we've initialized. */ + g_initialized_uart = true; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmc/pmc_api.cpp b/libraries/libexosphere/source/pmc/pmc_api.cpp new file mode 100644 index 000000000..67739e583 --- /dev/null +++ b/libraries/libexosphere/source/pmc/pmc_api.cpp @@ -0,0 +1,275 @@ +/* + * 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> + +namespace ams::pmc { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline u32 WriteMask = 0x1; + constexpr inline u32 ReadMask = 0x2; + + enum class LockMode { + Read, + Write, + ReadWrite, + }; + + template<LockMode Mode> + constexpr inline u32 LockMask = [] { + switch (Mode) { + case LockMode::Read: return ReadMask; + case LockMode::Write: return WriteMask; + case LockMode::ReadWrite: return ReadMask | WriteMask; + default: __builtin_unreachable(); + } + }(); + + constexpr inline size_t NumSecureScratchRegisters = 120; + constexpr inline size_t NumSecureDisableRegisters = 8; + + template<size_t SecureScratch> requires (SecureScratch < NumSecureScratchRegisters) + constexpr inline std::pair<size_t, size_t> DisableRegisterIndex = [] { + if constexpr (SecureScratch < 8) { + return std::pair<size_t, size_t>{0, 4 + 2 * SecureScratch}; + } else { + constexpr size_t Relative = SecureScratch - 8; + return std::pair<size_t, size_t>{1 + (Relative / 16), 2 * (Relative % 16)}; + } + }(); + + struct LockInfo { + size_t scratch; + LockMode mode; + }; + + template<LockInfo Info> + constexpr ALWAYS_INLINE void SetSecureScratchMask(std::array<u32, NumSecureDisableRegisters> &disables) { + constexpr std::pair<size_t, size_t> Location = DisableRegisterIndex<Info.scratch>; + disables[Location.first] |= LockMask<Info.mode> << Location.second; + } + + template<LockInfo... Info> + constexpr ALWAYS_INLINE void SetSecureScratchMasks(std::array<u32, NumSecureDisableRegisters> &disables) { + (SetSecureScratchMask<Info>(disables), ...); + } + + template<size_t... Ix> + constexpr ALWAYS_INLINE void SetSecureScratchReadWriteMasks(std::array<u32, NumSecureDisableRegisters> &disables) { + (SetSecureScratchMask<LockInfo{Ix, LockMode::ReadWrite}>(disables), ...); + } + + template<size_t... Ix> + constexpr ALWAYS_INLINE void SetSecureScratchReadMasks(std::array<u32, NumSecureDisableRegisters> &disables) { + (SetSecureScratchMask<LockInfo{Ix, LockMode::Read}>(disables), ...); + } + + template<size_t... Ix> + constexpr ALWAYS_INLINE void SetSecureScratchWriteMasks(std::array<u32, NumSecureDisableRegisters> &disables) { + (SetSecureScratchMask<LockInfo{Ix, LockMode::Write}>(disables), ...); + } + + template<SecureRegister Register> + constexpr ALWAYS_INLINE std::array<u32, NumSecureDisableRegisters> GetSecureScratchMasks() { + std::array<u32, NumSecureDisableRegisters> disables = {}; + + if constexpr ((Register & SecureRegister_Other) != 0) { + constexpr std::array<u32, NumSecureDisableRegisters> NonOtherDisables = GetSecureScratchMasks<static_cast<SecureRegister>(~SecureRegister_Other)>(); + for (size_t i = 0; i < NumSecureDisableRegisters; i++) { + disables[i] |= ~NonOtherDisables[i]; + } + disables[0] &= 0x007FFFF0; + } + if constexpr ((Register & SecureRegister_DramParameters) != 0) { + SetSecureScratchReadWriteMasks< 8, 9, 10, 11, 12, 13, 14, 15, + 17, 18, 19, 20, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 52, 53, 54, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 79, 80, 81, 82, 83, 84, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 104, 105, 106, 107 + >(disables); + } + if constexpr ((Register & SecureRegister_ResetVector) != 0) { + SetSecureScratchReadWriteMasks<34, 35>(disables); + } + if constexpr ((Register & SecureRegister_Carveout) != 0) { + SetSecureScratchReadWriteMasks<16, 39, 51, 55, 74, 75, 76, 77, 78, 99, 100, 101, 102, 103>(disables); + } + if constexpr ((Register & SecureRegister_CmacWrite) != 0) { + SetSecureScratchWriteMasks<112, 113, 114, 115>(disables); + } + if constexpr ((Register & SecureRegister_CmacRead) != 0) { + SetSecureScratchReadMasks<112, 113, 114, 115>(disables); + } + if constexpr ((Register & SecureRegister_KeySourceWrite) != 0) { + SetSecureScratchWriteMasks<24, 25, 26, 27>(disables); + } + if constexpr ((Register & SecureRegister_KeySourceRead) != 0) { + SetSecureScratchReadMasks<24, 25, 26, 27>(disables); + } + if constexpr ((Register & SecureRegister_Srk) != 0) { + SetSecureScratchReadWriteMasks<4, 5, 6, 7>(disables); + } + + return disables; + } + + /* Validate that the secure scratch masks produced are correct. */ + #include "pmc_secure_scratch_test.inc" + + ALWAYS_INLINE void LockBits(uintptr_t address, u32 mask) { + reg::Write(address, reg::Read(address) | mask); + } + + template<SecureRegister Register> + ALWAYS_INLINE void SetSecureScratchMasks(uintptr_t address) { + constexpr auto Masks = GetSecureScratchMasks<Register>(); + + if constexpr (Masks[0] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE , Masks[0]); } + if constexpr (Masks[1] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE2, Masks[1]); } + if constexpr (Masks[2] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE3, Masks[2]); } + if constexpr (Masks[3] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE4, Masks[3]); } + if constexpr (Masks[4] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE5, Masks[4]); } + if constexpr (Masks[5] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE6, Masks[5]); } + if constexpr (Masks[6] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE7, Masks[6]); } + if constexpr (Masks[7] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE8, Masks[7]); } + + static_assert(Masks.size() == 8); + } + + template<SecureRegister Register> + ALWAYS_INLINE bool TestSecureScratchMasks(uintptr_t address) { + constexpr auto Masks = GetSecureScratchMasks<Register>(); + + if constexpr (Masks[0] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE ) & Masks[0]) != Masks[0]) { return false; } } + if constexpr (Masks[1] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE2) & Masks[1]) != Masks[1]) { return false; } } + if constexpr (Masks[2] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE3) & Masks[2]) != Masks[2]) { return false; } } + if constexpr (Masks[3] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE4) & Masks[3]) != Masks[3]) { return false; } } + if constexpr (Masks[4] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE5) & Masks[4]) != Masks[4]) { return false; } } + if constexpr (Masks[5] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE6) & Masks[5]) != Masks[5]) { return false; } } + if constexpr (Masks[6] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE7) & Masks[6]) != Masks[6]) { return false; } } + if constexpr (Masks[7] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE8) & Masks[7]) != Masks[7]) { return false; } } + static_assert(Masks.size() == 8); + + return true; + } + + NOINLINE void WriteRandomValueToRegister(uintptr_t offset) { + /* Create an aligned buffer. */ + util::AlignedBuffer<hw::DataCacheLineSize, sizeof(u32)> buf; + + /* Generate random bytes into it. */ + se::GenerateRandomBytes(buf, sizeof(u32)); + + /* Read the random value. */ + const u32 random = *reinterpret_cast<const u32 *>(static_cast<u8 *>(buf)); + + /* Get the address. */ + const uintptr_t address = g_register_address + offset; + + /* Write the value. */ + reg::Write(address, random); + + /* Verify it was written. */ + AMS_ABORT_UNLESS(reg::Read(address) == random); + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void InitializeRandomScratch() { + /* Write random data to the scratch that contains the SRK. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH4); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH5); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH6); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH7); + + /* Lock the SRK scratch. */ + LockSecureRegister(SecureRegister_Srk); + + /* Write random data to the scratch used for tzram cmac. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH112); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH113); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH114); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH115); + + /* Write random data to the scratch used for tzram key source. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH24); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH25); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH26); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH27); + + /* Here, Nintendo locks the SRK scratch a second time. */ + /* This may just be "to be sure". */ + LockSecureRegister(SecureRegister_Srk); + } + + void LockSecureRegister(SecureRegister reg) { + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Apply each mask. */ + #define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { SetSecureScratchMasks<SecureRegister_##REG>(address); } } while (0) + PMC_PROCESS_REG(Other); + PMC_PROCESS_REG(DramParameters); + PMC_PROCESS_REG(ResetVector); + PMC_PROCESS_REG(Carveout); + PMC_PROCESS_REG(CmacWrite); + PMC_PROCESS_REG(CmacRead); + PMC_PROCESS_REG(KeySourceWrite); + PMC_PROCESS_REG(KeySourceRead); + PMC_PROCESS_REG(Srk); + #undef PMC_PROCESS_REG + + } + + LockState GetSecureRegisterLockState(SecureRegister reg) { + bool all_valid = true; + bool any_valid = false; + + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Test each mask. */ + #define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { const bool test = TestSecureScratchMasks<SecureRegister_##REG>(address); all_valid &= test; any_valid |= test; } } while (0) + PMC_PROCESS_REG(Other); + PMC_PROCESS_REG(DramParameters); + PMC_PROCESS_REG(ResetVector); + PMC_PROCESS_REG(Carveout); + PMC_PROCESS_REG(CmacWrite); + PMC_PROCESS_REG(CmacRead); + PMC_PROCESS_REG(KeySourceWrite); + PMC_PROCESS_REG(KeySourceRead); + PMC_PROCESS_REG(Srk); + #undef PMC_PROCESS_REG + + if (all_valid) { + return LockState::Locked; + } else if (any_valid) { + return LockState::PartiallyLocked; + } else { + return LockState::NotLocked; + } + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc b/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc new file mode 100644 index 000000000..b33268a24 --- /dev/null +++ b/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc @@ -0,0 +1,100 @@ +/* + * 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/>. + */ + +namespace test { + + constexpr inline auto Other = GetSecureScratchMasks<SecureRegister_Other>(); + static_assert(Other[0] == 0x00700FF0u); + static_assert(Other[1] == 0xFC000000u); + static_assert(Other[2] == 0x3F0FFF00u); + static_assert(Other[3] == 0x00000000u); + static_assert(Other[4] == 0x00000000u); + static_assert(Other[5] == 0x0C000000u); + static_assert(Other[6] == 0x00000000u); + static_assert(Other[7] == 0xFF00FF00u); + + constexpr inline auto DramParameters = GetSecureScratchMasks<SecureRegister_DramParameters>(); + static_assert(DramParameters[0] == 0x00000000u); + static_assert(DramParameters[1] == 0x03FCFFFFu); + static_assert(DramParameters[2] == 0x00000000u); + static_assert(DramParameters[3] == 0x3F3FFFFFu); + static_assert(DramParameters[4] == 0xFFFFFFFFu); + static_assert(DramParameters[5] == 0xF3FFC00Fu); + static_assert(DramParameters[6] == 0x003FFFFFu); + static_assert(DramParameters[7] == 0x000000FFu); + + constexpr inline auto ResetVector = GetSecureScratchMasks<SecureRegister_ResetVector>(); + static_assert(ResetVector[0] == 0x00000000u); + static_assert(ResetVector[1] == 0x00000000u); + static_assert(ResetVector[2] == 0x00F00000u); + static_assert(ResetVector[3] == 0x00000000u); + static_assert(ResetVector[4] == 0x00000000u); + static_assert(ResetVector[5] == 0x00000000u); + static_assert(ResetVector[6] == 0x00000000u); + static_assert(ResetVector[7] == 0x00000000u); + + constexpr inline auto CmacWrite = GetSecureScratchMasks<SecureRegister_CmacWrite>(); + static_assert(CmacWrite[0] == 0x00000000u); + static_assert(CmacWrite[1] == 0x00000000u); + static_assert(CmacWrite[2] == 0x00000000u); + static_assert(CmacWrite[3] == 0x00000000u); + static_assert(CmacWrite[4] == 0x00000000u); + static_assert(CmacWrite[5] == 0x00000000u); + static_assert(CmacWrite[6] == 0x00000000u); + static_assert(CmacWrite[7] == 0x00550000u); + + constexpr inline auto CmacRead = GetSecureScratchMasks<SecureRegister_CmacRead>(); + static_assert(CmacRead[0] == 0x00000000u); + static_assert(CmacRead[1] == 0x00000000u); + static_assert(CmacRead[2] == 0x00000000u); + static_assert(CmacRead[3] == 0x00000000u); + static_assert(CmacRead[4] == 0x00000000u); + static_assert(CmacRead[5] == 0x00000000u); + static_assert(CmacRead[6] == 0x00000000u); + static_assert(CmacRead[7] == 0x00AA0000u); + + constexpr inline auto KeySourceWrite = GetSecureScratchMasks<SecureRegister_KeySourceWrite>(); + static_assert(KeySourceWrite[0] == 0x00000000u); + static_assert(KeySourceWrite[1] == 0x00000000u); + static_assert(KeySourceWrite[2] == 0x00000055u); + static_assert(KeySourceWrite[3] == 0x00000000u); + static_assert(KeySourceWrite[4] == 0x00000000u); + static_assert(KeySourceWrite[5] == 0x00000000u); + static_assert(KeySourceWrite[6] == 0x00000000u); + static_assert(KeySourceWrite[7] == 0x00000000u); + + constexpr inline auto KeySourceRead = GetSecureScratchMasks<SecureRegister_KeySourceRead>(); + static_assert(KeySourceRead[0] == 0x00000000u); + static_assert(KeySourceRead[1] == 0x00000000u); + static_assert(KeySourceRead[2] == 0x000000AAu); + static_assert(KeySourceRead[3] == 0x00000000u); + static_assert(KeySourceRead[4] == 0x00000000u); + static_assert(KeySourceRead[5] == 0x00000000u); + static_assert(KeySourceRead[6] == 0x00000000u); + static_assert(KeySourceRead[7] == 0x00000000u); + + constexpr inline auto Srk = GetSecureScratchMasks<SecureRegister_Srk>(); + static_assert(Srk[0] == 0x000FF000u); + static_assert(Srk[1] == 0x00000000u); + static_assert(Srk[2] == 0x00000000u); + static_assert(Srk[3] == 0x00000000u); + static_assert(Srk[4] == 0x00000000u); + static_assert(Srk[5] == 0x00000000u); + static_assert(Srk[6] == 0x00000000u); + static_assert(Srk[7] == 0x00000000u); + + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmic/max77620.h b/libraries/libexosphere/source/pmic/max77620.h new file mode 100644 index 000000000..b1335544e --- /dev/null +++ b/libraries/libexosphere/source/pmic/max77620.h @@ -0,0 +1,340 @@ +/* + * Defining registers address and its bit definitions of MAX77620 and MAX20024 + * + * 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 + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _MFD_MAX77620_H_ +#define _MFD_MAX77620_H_ + +#define MAX77620_I2C_ADDR 0x3C + +/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ +#define MAX77620_REG_CNFGGLBL1 0x00 +#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_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 +#define MAX77620_REG_CNFGGLBL3 0x02 +#define MAX77620_WDTC_MASK 0x3 +#define MAX77620_WDTOFFC (1 << 4) +#define MAX77620_WDTSLPC (1 << 3) +#define MAX77620_WDTEN (1 << 2) +#define MAX77620_TWD_MASK 0x3 +#define MAX77620_TWD_2s 0x0 +#define MAX77620_TWD_16s 0x1 +#define MAX77620_TWD_64s 0x2 +#define MAX77620_TWD_128s 0x3 + +#define MAX77620_REG_CNFG1_32K 0x03 +#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) + +#define MAX77620_REG_CNFGBBC 0x04 +#define MAX77620_CNFGBBC_ENABLE (1 << 0) +#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 +#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 +#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 +#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 +#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) +#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 +#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 +#define MAX77620_CNFGBBC_RESISTOR_100 (0 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_1K (1 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_3K (2 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_6K (3 << MAX77620_CNFGBBC_RESISTOR_SHIFT) + +#define MAX77620_REG_IRQTOP 0x05 +#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) +#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) +#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) +#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) +#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) +#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) +#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) + +#define MAX77620_REG_INTLBT 0x06 +#define MAX77620_REG_IRQTOPM 0x0D +#define MAX77620_IRQ_LBM_MASK (1 << 3) +#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) +#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) + +#define MAX77620_REG_IRQSD 0x07 +#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 +#define MAX77620_REG_IRQ_LVL2_L8 0x09 +#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A +#define MAX77620_REG_ONOFFIRQ 0x0B +#define MAX77620_REG_NVERC 0x0C + +#define MAX77620_REG_INTENLBT 0x0E +#define MAX77620_GLBLM_MASK (1 << 0) + +#define MAX77620_REG_IRQMASKSD 0x0F +#define MAX77620_REG_IRQ_MSK_L0_7 0x10 +#define MAX77620_REG_IRQ_MSK_L8 0x11 +#define MAX77620_REG_ONOFFIRQM 0x12 +#define MAX77620_REG_STATLBT 0x13 +#define MAX77620_REG_STATSD 0x14 +#define MAX77620_REG_ONOFFSTAT 0x15 + +/* SD and LDO Registers */ +#define MAX77620_REG_SD0 0x16 +#define MAX77620_REG_SD1 0x17 +#define MAX77620_REG_SD2 0x18 +#define MAX77620_REG_SD3 0x19 +#define MAX77620_REG_SD4 0x1A +#define MAX77620_SDX_VOLT_MASK 0xFF +#define MAX77620_SD0_VOLT_MASK 0x3F +#define MAX77620_SD1_VOLT_MASK 0x7F +#define MAX77620_LDO_VOLT_MASK 0x3F +#define MAX77620_REG_DVSSD0 0x1B +#define MAX77620_REG_DVSSD1 0x1C +#define MAX77620_REG_SD0_CFG 0x1D +#define MAX77620_REG_SD1_CFG 0x1E +#define MAX77620_REG_SD2_CFG 0x1F +#define MAX77620_REG_SD3_CFG 0x20 +#define MAX77620_REG_SD4_CFG 0x21 +#define MAX77620_REG_SD_CFG2 0x22 +#define MAX77620_REG_LDO0_CFG 0x23 +#define MAX77620_REG_LDO0_CFG2 0x24 +#define MAX77620_REG_LDO1_CFG 0x25 +#define MAX77620_REG_LDO1_CFG2 0x26 +#define MAX77620_REG_LDO2_CFG 0x27 +#define MAX77620_REG_LDO2_CFG2 0x28 +#define MAX77620_REG_LDO3_CFG 0x29 +#define MAX77620_REG_LDO3_CFG2 0x2A +#define MAX77620_REG_LDO4_CFG 0x2B +#define MAX77620_REG_LDO4_CFG2 0x2C +#define MAX77620_REG_LDO5_CFG 0x2D +#define MAX77620_REG_LDO5_CFG2 0x2E +#define MAX77620_REG_LDO6_CFG 0x2F +#define MAX77620_REG_LDO6_CFG2 0x30 +#define MAX77620_REG_LDO7_CFG 0x31 +#define MAX77620_REG_LDO7_CFG2 0x32 +#define MAX77620_REG_LDO8_CFG 0x33 +#define MAX77620_REG_LDO8_CFG2 0x34 +#define MAX77620_LDO_POWER_MODE_MASK 0xC0 +#define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX77620_POWER_MODE_NORMAL 3 +#define MAX77620_POWER_MODE_LPM 2 +#define MAX77620_POWER_MODE_GLPM 1 +#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 << 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) +#define MAX77620_LDO_CFG2_SS_SLOW 0 + +#define MAX77620_REG_LDO_CFG3 0x35 +#define MAX77620_TRACK4_MASK (1 << 5) +#define MAX77620_TRACK4_SHIFT 5 + +#define MAX77620_LDO_SLEW_RATE_MASK 0x1 + +#define MAX77620_REG_GPIO0 0x36 +#define MAX77620_REG_GPIO1 0x37 +#define MAX77620_REG_GPIO2 0x38 +#define MAX77620_REG_GPIO3 0x39 +#define MAX77620_REG_GPIO4 0x3A +#define MAX77620_REG_GPIO5 0x3B +#define MAX77620_REG_GPIO6 0x3C +#define MAX77620_REG_GPIO7 0x3D +#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) +#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 +#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 +#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) +#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) +#define MAX20024_ONOFFCNFG1_CLRSE 0x18 + +#define MAX77620_REG_ONOFFCNFG2 0x42 +#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) +#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) +#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) +#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) +#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) + +/* FPS Registers */ +#define MAX77620_REG_FPS_CFG0 0x43 +#define MAX77620_REG_FPS_CFG1 0x44 +#define MAX77620_REG_FPS_CFG2 0x45 +#define MAX77620_REG_FPS_LDO0 0x46 +#define MAX77620_REG_FPS_LDO1 0x47 +#define MAX77620_REG_FPS_LDO2 0x48 +#define MAX77620_REG_FPS_LDO3 0x49 +#define MAX77620_REG_FPS_LDO4 0x4A +#define MAX77620_REG_FPS_LDO5 0x4B +#define MAX77620_REG_FPS_LDO6 0x4C +#define MAX77620_REG_FPS_LDO7 0x4D +#define MAX77620_REG_FPS_LDO8 0x4E +#define MAX77620_REG_FPS_SD0 0x4F +#define MAX77620_REG_FPS_SD1 0x50 +#define MAX77620_REG_FPS_SD2 0x51 +#define MAX77620_REG_FPS_SD3 0x52 +#define MAX77620_REG_FPS_SD4 0x53 +#define MAX77620_REG_FPS_NONE 0 +#define MAX77620_FPS_SRC_MASK 0xC0 +#define MAX77620_FPS_SRC_SHIFT 6 +#define MAX77620_FPS_PU_PERIOD_MASK 0x38 +#define MAX77620_FPS_PU_PERIOD_SHIFT 3 +#define MAX77620_FPS_PD_PERIOD_MASK 0x07 +#define MAX77620_FPS_PD_PERIOD_SHIFT 0 + +/* Minimum and maximum FPS period time (in microseconds) are + * different for MAX77620 and Max20024. + */ +#define MAX77620_FPS_COUNT 3 + +#define MAX77620_FPS_PERIOD_MIN_US 40 +#define MAX20024_FPS_PERIOD_MIN_US 20 + +#define MAX77620_FPS_PERIOD_MAX_US 2560 +#define MAX20024_FPS_PERIOD_MAX_US 5120 + +#define MAX77620_REG_FPS_GPIO1 0x54 +#define MAX77620_REG_FPS_GPIO2 0x55 +#define MAX77620_REG_FPS_GPIO3 0x56 +#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 +#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 +#define MAX77620_FPS_EN_SRC_MASK 0x06 +#define MAX77620_FPS_EN_SRC_SHIFT 1 +#define MAX77620_FPS_ENFPS_SW_MASK 0x01 +#define MAX77620_FPS_ENFPS_SW 0x01 + +#define MAX77620_REG_FPS_RSO 0x57 +#define MAX77620_REG_CID0 0x58 +#define MAX77620_REG_CID1 0x59 +#define MAX77620_REG_CID2 0x5A +#define MAX77620_REG_CID3 0x5B +#define MAX77620_REG_CID4 0x5C +#define MAX77620_REG_CID5 0x5D + +#define MAX77620_REG_DVSSD4 0x5E +#define MAX20024_REG_MAX_ADD 0x70 + +#define MAX77620_CID_DIDM_MASK 0xF0 +#define MAX77620_CID_DIDM_SHIFT 4 + +/* CNCG2SD */ +#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) +#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) + +/* Device Identification Metal */ +#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) +/* Device Indentification OTP */ +#define MAX77620_CID5_DIDO(n) ((n) & 0xF) + +/* SD CNFG1 */ +#define MAX77620_SD_SR_MASK 0xC0 +#define MAX77620_SD_SR_SHIFT 6 +#define MAX77620_SD_POWER_MODE_MASK 0x30 +#define MAX77620_SD_POWER_MODE_SHIFT 4 +#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) +#define MAX77620_SD_CFG1_ADE_DISABLE 0 +#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) +#define MAX77620_SD_FPWM_MASK 0x04 +#define MAX77620_SD_FPWM_SHIFT 2 +#define MAX77620_SD_FSRADE_MASK 0x01 +#define MAX77620_SD_FSRADE_SHIFT 0 +#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) +#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 +#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) +#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) +#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) +#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 +#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) + +#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) +#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) +#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) +#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) +#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) +#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) +#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) +#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) + +/* Interrupts */ +enum { + MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ + MAX77620_IRQ_TOP_SD, /* SD power fail */ + MAX77620_IRQ_TOP_LDO, /* LDO power fail */ + MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ + MAX77620_IRQ_TOP_RTC, /* RTC */ + MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ + MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ + MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ +}; + +/* GPIOs */ +enum { + MAX77620_GPIO0, + MAX77620_GPIO1, + MAX77620_GPIO2, + MAX77620_GPIO3, + MAX77620_GPIO4, + MAX77620_GPIO5, + MAX77620_GPIO6, + MAX77620_GPIO7, + MAX77620_GPIO_NR, +}; + +/* FPS Source */ +enum max77620_fps_src { + MAX77620_FPS_SRC_0, + MAX77620_FPS_SRC_1, + MAX77620_FPS_SRC_2, + MAX77620_FPS_SRC_NONE, + MAX77620_FPS_SRC_DEF, +}; + +enum max77620_chip_id { + MAX77620, + MAX20024, +}; + +#endif /* _MFD_MAX77620_H_ */ \ No newline at end of file diff --git a/libraries/libexosphere/source/pmic/max7762x.h b/libraries/libexosphere/source/pmic/max7762x.h new file mode 100644 index 000000000..1c8202515 --- /dev/null +++ b/libraries/libexosphere/source/pmic/max7762x.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 naehrwert + * 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/>. + */ + +#ifndef _MAX7762X_H_ +#define _MAX7762X_H_ + +/* +* 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) +* 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) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | +* 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 | +*/ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +/*! MAX77620 partitions. */ +#define REGULATOR_SD0 0 +#define REGULATOR_SD1 1 +#define REGULATOR_SD2 2 +#define REGULATOR_SD3 3 +#define REGULATOR_LDO0 4 +#define REGULATOR_LDO1 5 +#define REGULATOR_LDO2 6 +#define REGULATOR_LDO3 7 +#define REGULATOR_LDO4 8 +#define REGULATOR_LDO5 9 +#define REGULATOR_LDO6 10 +#define REGULATOR_LDO7 11 +#define REGULATOR_LDO8 12 +#define REGULATOR_MAX 12 + +#define MAX77621_CPU_I2C_ADDR 0x1B +#define MAX77621_GPU_I2C_ADDR 0x1C + +#define MAX77621_VOUT_REG 0 +#define MAX77621_VOUT_DVC_REG 1 +#define MAX77621_CONTROL1_REG 2 +#define MAX77621_CONTROL2_REG 3 + +/* MAX77621_VOUT */ +#define MAX77621_VOUT_DISABLE (0 << 7) +#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 + +/* MAX77621_CONTROL1 */ +#define MAX77621_SNS_ENABLE (1 << 7) +#define MAX77621_FPWM_EN_M (1 << 6) +#define MAX77621_NFSR_ENABLE (1 << 5) +#define MAX77621_AD_ENABLE (1 << 4) +#define MAX77621_BIAS_ENABLE (1 << 3) +#define MAX77621_FREQSHIFT_9PER (1 << 2) + +#define MAX77621_RAMP_12mV_PER_US 0x0 +#define MAX77621_RAMP_25mV_PER_US 0x1 +#define MAX77621_RAMP_50mV_PER_US 0x2 +#define MAX77621_RAMP_200mV_PER_US 0x3 +#define MAX77621_RAMP_MASK 0x3 + +/* MAX77621_CONTROL2 */ +#define MAX77621_WDTMR_ENABLE (1 << 6) +#define MAX77621_DISCH_ENBABLE (1 << 5) +#define MAX77621_FT_ENABLE (1 << 4) +#define MAX77621_T_JUNCTION_120 (1 << 7) + +#define MAX77621_CKKADV_TRIP_DISABLE 0xC +#define MAX77621_CKKADV_TRIP_75mV_PER_US 0x0 +#define MAX77621_CKKADV_TRIP_150mV_PER_US 0x4 +#define MAX77621_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8 + +#define MAX77621_INDUCTOR_MIN_30_PER 0x0 +#define MAX77621_INDUCTOR_NOMINAL 0x1 +#define MAX77621_INDUCTOR_PLUS_30_PER 0x2 +#define MAX77621_INDUCTOR_PLUS_60_PER 0x3 + +#endif diff --git a/libraries/libexosphere/source/pmic/pmic_api.cpp b/libraries/libexosphere/source/pmic/pmic_api.cpp new file mode 100644 index 000000000..e50723dc6 --- /dev/null +++ b/libraries/libexosphere/source/pmic/pmic_api.cpp @@ -0,0 +1,135 @@ +/* + * 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 "max77620.h" +#include "max7762x.h" + +namespace ams::pmic { + + namespace { + + constexpr inline int I2cAddressEristaMax77621 = 0x1B; + constexpr inline int I2cAddressMarikoMax77812_A = 0x31; + constexpr inline int I2cAddressMarikoMax77812_B = 0x33; + + + /* https://github.com/Atmosphere-NX/Atmosphere/blob/master/emummc/source/power/max7762x.h */ + /* TODO: Find datasheet, link to it instead. */ + /* NOTE: Tentatively, Max77620 "mostly" matches https://datasheets.maximintegrated.com/en/ds/MAX77863.pdf. */ + /* This does not contain Max77621 documentation, though. */ + constexpr inline int Max77620RegisterGpio0 = 0x36; + constexpr inline int Max77620RegisterAmeGpio = 0x40; + + constexpr inline int Max77621RegisterVOut = 0x00; + constexpr inline int Max77621RegisterVOutDvc = 0x01; + constexpr inline int Max77621RegisterControl1 = 0x02; + constexpr inline int Max77621RegisterControl2 = 0x03; + + + /* https://datasheets.maximintegrated.com/en/ds/MAX77812.pdf */ + constexpr inline int Max77812RegisterEnCtrl = 0x06; + constexpr inline int Max77812RegisterM4VOut = 0x26; + + void Max77620EnableGpio(int gpio) { + u8 val; + + /* Clear the AE for the GPIO */ + if (i2c::Query(std::addressof(val), sizeof(val), i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio)) { + val &= ~(1 << gpio); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio, val); + } + + /* Set GPIO_DRV_PUSHPULL (bit 0), GPIO_OUTPUT_VAL_HIGH (bit 3). */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterGpio0 + gpio, MAX77620_CNFG_GPIO_DRV_PUSHPULL | MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH); + } + + void EnableVddCpuErista() { + /* Enable GPIO 5. */ + /* TODO: What does this control? */ + Max77620EnableGpio(5); + + /* Configure Max77621 control registers. */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl1, MAX77621_AD_ENABLE | MAX77621_NFSR_ENABLE | MAX77621_SNS_ENABLE | MAX77621_RAMP_12mV_PER_US); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl2, MAX77621_T_JUNCTION_120 | MAX77621_WDTMR_ENABLE | MAX77621_CKKADV_TRIP_75mV_PER_US| MAX77621_INDUCTOR_NOMINAL); + + /* Configure Max77621 VOut to 0.95v */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOutDvc, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V); + } + + void DisableVddCpuErista() { + /* Disable Max77621 VOut. */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_DISABLE); + } + + int GetI2cAddressForMarikoMax77812(Regulator regulator) { + switch (regulator) { + case Regulator_Mariko_Max77812_A: return I2cAddressMarikoMax77812_A; + case Regulator_Mariko_Max77812_B: return I2cAddressMarikoMax77812_B; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void EnableVddCpuMariko(Regulator regulator) { + const int address = GetI2cAddressForMarikoMax77812(regulator); + + /* Set EN_M3_LPM to enable BUCK Master 3 low power mode. */ + u8 ctrl; + if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) { + ctrl |= 0x40; + i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl); + } + + /* Set BUCK Master 4 output voltage to 110. */ + i2c::SendByte(i2c::Port_5, address, Max77812RegisterM4VOut, 110); + } + + void DisableVddCpuMariko(Regulator regulator) { + const int address = GetI2cAddressForMarikoMax77812(regulator); + + /* Clear EN_M3_LPM to disable BUCK Master 3 low power mode. */ + u8 ctrl; + if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) { + ctrl &= ~0x40; + i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl); + } + } + + } + + void EnableVddCpu(Regulator regulator) { + switch (regulator) { + case Regulator_Erista_Max77621: + return EnableVddCpuErista(); + case Regulator_Mariko_Max77812_A: + case Regulator_Mariko_Max77812_B: + return EnableVddCpuMariko(regulator); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void DisableVddCpu(Regulator regulator) { + switch (regulator) { + case Regulator_Erista_Max77621: + return DisableVddCpuErista(); + case Regulator_Mariko_Max77812_A: + case Regulator_Mariko_Max77812_B: + return DisableVddCpuMariko(regulator); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp new file mode 100644 index 000000000..f9aac61b8 --- /dev/null +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -0,0 +1,209 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constexpr inline int AesKeySizeMax = 256 / BITSIZEOF(u8); + + enum AesMode { + AesMode_Aes128 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY128 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY128 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + AesMode_Aes192 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY192 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY192 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + AesMode_Aes256 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY256 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY256 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + }; + + enum MemoryInterface { + MemoryInterface_Ahb = SE_CRYPTO_CONFIG_MEMIF_AHB, + MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF, + }; + + constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) { + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM_SEL(CONFIG_ENC_ALG, encrypt, AES_ENC, NOP), + SE_REG_BITS_ENUM_SEL(CONFIG_DEC_ALG, encrypt, NOP, AES_DEC), + SE_REG_BITS_VALUE (CONFIG_DST, dst)); + } + + void SetAesConfig(volatile SecurityEngineRegisters *SE, int slot, bool encrypt, u32 config) { + const u32 encoded = reg::Encode(SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB), + SE_REG_BITS_VALUE (CRYPTO_CONFIG_KEY_INDEX, slot), + SE_REG_BITS_ENUM_SEL(CRYPTO_CONFIG_CORE_SEL, encrypt, ENCRYPT, DECRYPT)); + + reg::Write(SE->SE_CRYPTO_CONFIG, (config | encoded)); + } + + void SetBlockCount(volatile SecurityEngineRegisters *SE, int count) { + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, count - 1); + } + + void UpdateAesMode(volatile SecurityEngineRegisters *SE, AesMode mode) { + reg::ReadWrite(SE->SE_CONFIG, REG_BITS_VALUE(16, 16, mode)); + } + + // void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { + // reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); + // } + + void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) { + AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); + AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount); + AMS_ABORT_UNLESS(0 <= kek_slot && kek_slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for single AES ECB decryption to key table. */ + SetConfig(SE, false, SE_CONFIG_DST_KEYTABLE); + SetAesConfig(SE, kek_slot, false, AesConfigEcb); + UpdateAesMode(SE, mode); + SetBlockCount(SE, 1); + + /* Select the destination keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, dst_slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3)); + + /* Ensure that the se sees the keydata we want it to. */ + hw::FlushDataCache(key, key_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, key, key_size); + } + + void EncryptAes(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, AesMode mode) { + /* If nothing to decrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(dst_size == AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for AES-ECB encryption to memory. */ + SetConfig(SE, true, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, true, AesConfigEcb); + UpdateAesMode(SE, mode); + + /* Execute the operation. */ + ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); + } + + } + + void ClearAesKeySlot(int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + for (int i = 0; i < 16; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, i)); + + /* Write the data. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + } + + void LockAesKeySlot(int slot, u32 flags) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set non per-key flags. */ + if ((flags & ~KeySlotLockFlags_PerKey) != 0) { + /* TODO: KeySlotLockFlags_DstKeyTableOnly is Mariko-only. How should we handle this? */ + /* TODO: Mariko bit support. */ + reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags)); + } + + /* Set per-key flag. */ + if ((flags & KeySlotLockFlags_PerKey) != 0) { + reg::ReadWrite(SE->SE_CRYPTO_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0)); + } + } + + void SetAesKey(int slot, const void *key, size_t key_size) { + /* Validate the key slot and key size. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set each key word in order. */ + const u32 *key_u32 = static_cast<const u32 *>(key); + const int num_words = key_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, KEY), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the key word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = *(key_u32++); + } + } + + void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size) { + return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes128); + } + + void SetEncryptedAesKey256(int dst_slot, int kek_slot, const void *key, size_t key_size) { + return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes256); + } + + void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return EncryptAes(dst, dst_size, slot, src, src_size, AesMode_Aes128); + } + + void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + /* If nothing to decrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(dst_size == AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for AES-ECB decryption to memory. */ + SetConfig(SE, false, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, false, AesConfigEcb); + + ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); + } + +} diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp new file mode 100644 index 000000000..dbe3cbcc7 --- /dev/null +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -0,0 +1,138 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + struct LinkedListEntry { + u32 zero; + u32 address; + u32 size; + }; + static_assert(util::is_pod<LinkedListEntry>::value); + + uintptr_t GetPhysicalAddress(const void *ptr) { + const uintptr_t virt_address = reinterpret_cast<uintptr_t>(ptr); + + #if defined(ATMOSPHERE_ARCH_ARM64) + u64 phys_address; + __asm__ __volatile__("at s1e3r, %[virt]; mrs %[phys], par_el1" : [phys]"=r"(phys_address) : [virt]"r"(virt_address) : "memory", "cc"); + return (phys_address & 0x0000FFFFFFFFF000ul) | (virt_address & 0x0000000000000FFFul); + #elif defined(ATMOSPHERE_ARCH_ARM) + return virt_address; + #else + #error "Unknown architecture for Tegra Security Engine physical address translation" + #endif + } + + constexpr void SetLinkedListEntry(LinkedListEntry *entry, const void *ptr, size_t size) { + /* Clear the zero field. */ + entry->zero = 0; + + /* Set the address. */ + if (ptr != nullptr) { + entry->address = GetPhysicalAddress(ptr); + entry->size = static_cast<u32>(size); + } else { + entry->address = 0; + entry->size = 0; + } + } + + void StartOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op) { + /* Write back the current values of the error and interrupt status. */ + reg::Write(SE->SE_ERR_STATUS, reg::Read(SE->SE_ERR_STATUS)); + reg::Write(SE->SE_INT_STATUS, reg::Read(SE->SE_INT_STATUS)); + + /* Write the operation. */ + reg::Write(SE->SE_OPERATION, SE_REG_BITS_VALUE(OPERATION_OP, op)); + } + + void WaitForOperationComplete(volatile SecurityEngineRegisters *SE) { + /* Spin until the operation is done. */ + while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_SE_OP_DONE, CLEAR))) { /* ... */ } + + /* Check for operation success. */ + ValidateAesOperationResult(SE); + } + + } + + void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Set the linked list entries. */ + LinkedListEntry src_entry; + LinkedListEntry dst_entry; + + SetLinkedListEntry(std::addressof(src_entry), src, src_size); + SetLinkedListEntry(std::addressof(dst_entry), dst, dst_size); + + /* Ensure the linked list entry data is seen correctly. */ + hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry)); + hw::FlushDataCache(std::addressof(dst_entry), sizeof(dst_entry)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(src_entry)))); + reg::Write(SE->SE_OUT_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(dst_entry)))); + + /* Start the operation. */ + StartOperation(SE, op); + + /* Wait for the operation to complete. */ + WaitForOperationComplete(SE); + } + + void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Validate sizes. */ + AMS_ABORT_UNLESS(dst_size <= AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + + /* Set the block count to 1. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Create an aligned buffer. */ + util::AlignedBuffer<hw::DataCacheLineSize, AesBlockSize> aligned; + std::memcpy(aligned, src, AesBlockSize); + hw::FlushDataCache(aligned, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, aligned, AesBlockSize, aligned, AesBlockSize); + + /* Ensure that the CPU will see the correct output. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(aligned, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the output to the destination. */ + std::memcpy(dst, aligned, dst_size); + } + + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE) { + /* Ensure no error occurred. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); + + /* Ensure the security engine is idle. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); + + /* Ensure there is no error status. */ + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + } + +} diff --git a/libraries/libexosphere/source/se/se_execute.hpp b/libraries/libexosphere/source/se/se_execute.hpp new file mode 100644 index 000000000..76b0a34af --- /dev/null +++ b/libraries/libexosphere/source/se/se_execute.hpp @@ -0,0 +1,28 @@ +/* + * 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 "se_registers.hpp" + +namespace ams::se { + + volatile SecurityEngineRegisters *GetRegisters(); + + void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); + void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE); + +} diff --git a/libraries/libexosphere/source/se/se_management.cpp b/libraries/libexosphere/source/se/se_management.cpp new file mode 100644 index 000000000..de845c2f0 --- /dev/null +++ b/libraries/libexosphere/source/se/se_management.cpp @@ -0,0 +1,114 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetAddress(); + constinit DoneHandler g_done_handler = nullptr; + + } + + volatile SecurityEngineRegisters *GetRegisters() { + return reinterpret_cast<volatile SecurityEngineRegisters *>(g_register_address); + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void Initialize() { + auto *SE = GetRegisters(); + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); + } + + void SetSecure(bool secure) { + auto *SE = GetRegisters(); + + /* Set the security software setting. */ + if (secure) { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); + } else { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, NONSECURE)); + } + + /* Read the status register to force an update. */ + reg::Read(SE->SE_SE_SECURITY); + } + + void SetTzramSecure() { + auto *SE = GetRegisters(); + + /* Set the TZRAM setting to secure. */ + SE->SE_TZRAM_SECURITY = SE_TZRAM_SETTING_SECURE; + } + + void SetPerKeySecure() { + auto *SE = GetRegisters(); + + /* Clear AES PerKey security. */ + SE->SE_CRYPTO_SECURITY_PERKEY = 0; + + /* Clear RSA PerKey security. */ + SE->SE_RSA_SECURITY_PERKEY = 0; + + /* Update PERKEY_SETTING to secure. */ + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE)); + } + + void Lockout() { + auto *SE = GetRegisters(); + + /* Lock access to the AES keyslots. */ + for (int i = 0; i < AesKeySlotCount; ++i) { + SE->SE_CRYPTO_KEYTABLE_ACCESS[i] = 0; + } + + /* Lock access to the RSA keyslots. */ + for (int i = 0; i < RsaKeySlotCount; ++i) { + SE->SE_RSA_KEYTABLE_ACCESS[i] = 0; + } + + /* Set Per Key secure. */ + SetPerKeySecure(); + + /* Configure SE_SECURITY. */ + { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_HARD_SETTING, SECURE), + SE_REG_BITS_ENUM(SECURITY_ENG_DIS, DISABLE), + SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE), + SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); + } + } + + void HandleInterrupt() { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Disable the SE interrupt. */ + reg::Write(SE->SE_INT_ENABLE, 0); + + /* Execute the handler if we have one. */ + if (const auto handler = g_done_handler; handler != nullptr) { + g_done_handler = nullptr; + handler(); + } + } + +} diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp new file mode 100644 index 000000000..d76e0571c --- /dev/null +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -0,0 +1,249 @@ +/* + * 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> + +namespace ams::se { + + struct SecurityEngineRegisters { + u32 SE_SE_SECURITY; + u32 SE_TZRAM_SECURITY; + u32 SE_OPERATION; + u32 SE_INT_ENABLE; + u32 SE_INT_STATUS; + u32 SE_CONFIG; + u32 SE_IN_LL_ADDR; + u32 SE_IN_CUR_BYTE_ADDR; + u32 SE_IN_CUR_LL_ID; + u32 SE_OUT_LL_ADDR; + u32 SE_OUT_CUR_BYTE_ADDR; + u32 SE_OUT_CUR_LL_ID; + u32 SE_HASH_RESULT[0x10]; + u32 SE_CTX_SAVE_CONFIG; + u32 _0x74[0x63]; + u32 SE_SHA_CONFIG; + u32 SE_SHA_MSG_LENGTH[0x4]; + u32 SE_SHA_MSG_LEFT[0x4]; + u32 _0x224[0x17]; + u32 SE_CRYPTO_SECURITY_PERKEY; + u32 SE_CRYPTO_KEYTABLE_ACCESS[0x10]; + u32 _0x2C4[0x10]; + u32 SE_CRYPTO_CONFIG; + u32 SE_CRYPTO_LINEAR_CTR[0x4]; + u32 SE_CRYPTO_LAST_BLOCK; + u32 SE_CRYPTO_KEYTABLE_ADDR; + u32 SE_CRYPTO_KEYTABLE_DATA; + u32 _0x324[0x3]; + u32 SE_CRYPTO_KEYTABLE_DST; + u32 _0x334[0x3]; + u32 SE_RNG_CONFIG; + u32 SE_RNG_SRC_CONFIG; + u32 SE_RNG_RESEED_INTERVAL; + u32 _0x34C[0x2D]; + u32 SE_RSA_CONFIG; + u32 SE_RSA_KEY_SIZE; + u32 SE_RSA_EXP_SIZE; + u32 SE_RSA_SECURITY_PERKEY; + u32 SE_RSA_KEYTABLE_ACCESS[0x2]; + u32 _0x418[0x2]; + u32 SE_RSA_KEYTABLE_ADDR; + u32 SE_RSA_KEYTABLE_DATA; + u32 SE_RSA_OUTPUT[0x40]; + u32 _0x528[0xB6]; + u32 SE_STATUS; + u32 SE_ERR_STATUS; + u32 SE_MISC; + u32 SE_SPARE; + u32 SE_ENTROPY_DEBUG_COUNTER; + u32 _0x814; + u32 _0x818; + u32 _0x81C; + u32 _0x820[0x5F8]; + }; + static_assert(util::is_pod<SecurityEngineRegisters>::value); + static_assert(sizeof(SecurityEngineRegisters) == secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetSize()); + + static_assert(AesKeySlotCount == util::size(SecurityEngineRegisters{}.SE_CRYPTO_KEYTABLE_ACCESS)); + static_assert(RsaKeySlotCount == util::size(SecurityEngineRegisters{}.SE_RSA_KEYTABLE_ACCESS)); + + #define SE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SE, NAME) + #define SE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SE, NAME, VALUE) + #define SE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SE, NAME, ENUM) + #define SE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_SE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SE, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_SE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_SE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_SE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_SE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + #define DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(NAME, __OFFSET__) \ + REG_DEFINE_NAMED_REG(SE, NAME, __OFFSET__, 1); \ + \ + enum SE_##NAME { \ + SE_##NAME##_##CLEAR = 0, \ + SE_##NAME##_##ACTIVE = 1, \ + SE_##NAME##_##SW_CLEAR = 1, \ + }; + + /* SE_STATUS. */ + DEFINE_SE_REG_TWO_BIT_ENUM(STATUS_STATE, 0, IDLE, BUSY, WAIT_OUT, WAIT_IN); + + /* SE_SECURITY */ + DEFINE_SE_REG_BIT_ENUM(SECURITY_HARD_SETTING, 0, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_ENG_DIS, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_PERKEY_SETTING, 2, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_SOFT_SETTING, 16, SECURE, NONSECURE); + + /* SE_TZRAM_SECURITY */ + DEFINE_SE_REG(TZRAM_SETTING, 0, BITSIZEOF(u32)); + constexpr inline u32 SE_TZRAM_SETTING_SECURE = 0; + + /* SE_OPERATION */ + DEFINE_SE_REG_THREE_BIT_ENUM(OPERATION_OP, 0, ABORT, START, RESTART_OUT, CTX_SAVE, RESTART_IN, RESERVED_5, RESERVED_6, RESERVED_7); + + /* SE_INT_ENABLE */ + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_LL_BUF_RD, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_DONE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_LL_BUF_WR, 2, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_DONE, 3, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_SE_OP_DONE, 4, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_RESEED_CNTR_EXHAUSTED, 5, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_ERR_STAT, 16, DISABLE, ENABLE); + + /* SE_INT_STATUS */ + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_LL_BUF_RD, 0); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_DONE, 1); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_LL_BUF_WR, 2); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_DONE, 3); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_SE_OP_DONE, 4); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_RESEED_CNTR_EXHAUSTED, 5); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_ERR_STAT, 16); + + /* SE_CONFIG */ + DEFINE_SE_REG(CONFIG_DST, 2, 3); + DEFINE_SE_REG(CONFIG_DEC_ALG, 8, 4); + DEFINE_SE_REG(CONFIG_ENC_ALG, 12, 4); + DEFINE_SE_REG(CONFIG_DEC_MODE, 16, 8); + DEFINE_SE_REG(CONFIG_ENC_MODE, 24, 8); + + enum SE_CONFIG_DST { + SE_CONFIG_DST_MEMORY = 0, + SE_CONFIG_DST_HASH_REG = 1, + SE_CONFIG_DST_KEYTABLE = 2, + SE_CONFIG_DST_SRK = 3, + SE_CONFIG_DST_RSA_REG = 4, + }; + + enum SE_CONFIG_DEC_ALG { + SE_CONFIG_DEC_ALG_NOP = 0, + SE_CONFIG_DEC_ALG_AES_DEC = 1, + }; + + enum SE_CONFIG_ENC_ALG { + SE_CONFIG_ENC_ALG_NOP = 0, + SE_CONFIG_ENC_ALG_AES_ENC = 1, + SE_CONFIG_ENC_ALG_RNG = 2, + SE_CONFIG_ENC_ALG_SHA = 3, + SE_CONFIG_ENC_ALG_RSA = 4, + }; + + enum SE_CONFIG_DEC_MODE { + SE_CONFIG_DEC_MODE_AESMODE_KEY128 = 0, + SE_CONFIG_DEC_MODE_AESMODE_KEY192 = 1, + SE_CONFIG_DEC_MODE_AESMODE_KEY256 = 2, + }; + + enum SE_CONFIG_ENC_MODE { + SE_CONFIG_ENC_MODE_AESMODE_KEY128 = 0, + SE_CONFIG_ENC_MODE_AESMODE_KEY192 = 1, + SE_CONFIG_ENC_MODE_AESMODE_KEY256 = 2, + + SE_CONFIG_ENC_MODE_AESMODE_SHA1 = 1, + SE_CONFIG_ENC_MODE_AESMODE_SHA224 = 4, + SE_CONFIG_ENC_MODE_AESMODE_SHA256 = 5, + SE_CONFIG_ENC_MODE_AESMODE_SHA384 = 6, + SE_CONFIG_ENC_MODE_AESMODE_SHA512 = 7, + }; + + + /* SE_CRYPTO_KEYTABLE_ADDR */ + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, 0, 4); + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_WORD, 0, 2); + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, 0, 3); + + enum SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD { + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_0 = 0u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_1 = 1u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_2 = 2u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_3 = 3u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_4 = 4u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_5 = 5u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_6 = 6u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_7 = 7u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_0 = 8u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_1 = 9u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_2 = 10u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_3 = 11u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_0 = 12u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_1 = 13u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_2 = 14u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_3 = 15u, + }; + + DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, 2, ORIGINAL_IV, UPDATED_IV); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, 3, KEY, IV); + + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, 4, 4); + + /* SE_RSA_KEYTABLE_ADDR */ + DEFINE_SE_REG(RSA_KEYTABLE_ADDR_WORD_ADDR, 0, 6); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_EXPMOD_SEL, 6, EXPONENT, MODULUS); + DEFINE_SE_REG(RSA_KEYTABLE_ADDR_KEY_SLOT, 7, 1); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_INPUT_MODE, 8, REGISTER, MEMORY); + + /* SE_RSA_KEYTABLE_ACCESS */ + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYREAD, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUPDATE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUSE, 2, DISABLE, ENABLE); + + /* SE_CRYPTO_CONFIG */ + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_HASH_ENB, 0, DISABLE, ENABLE); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_XOR_POS, 1, BYPASS, RESERVED, TOP, BOTTOM); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_INPUT_SEL, 3, MEMORY, RANDOM, INIT_AESOUT, LINEAR_CTR); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_VCTRAM_SEL, 5, MEMORY, RESERVED, INIT_AESOUT, INIT_PREV_MEMORY); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_IV_SELECT, 7, ORIGINAL, UPDATED); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_CORE_SEL, 8, DECRYPT, ENCRYPT); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_KEYSCH_BYPASS, 10, DISABLE, ENABLE); + DEFINE_SE_REG(CRYPTO_CONFIG_CTR_CNTN, 11, 8); + DEFINE_SE_REG(CRYPTO_CONFIG_KEY_INDEX, 24, 4); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_MEMIF, 31, AHB, MCCIF); + + /* SE_CRYPTO_KEYTABLE_DST */ + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, 0, KEYS_0_3, KEYS_4_7, ORIGINAL_IV, UPDATED_IV); + DEFINE_SE_REG(CRYPTO_KEYTABLE_DST_KEY_INDEX, 8, 4); + + /* SE_RNG_CONFIG */ + DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_MODE, 0, NORMAL, FORCE_INSTANTIATION, FORCE_RESEED, RESERVED3); + DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_SRC, 2, NONE, ENTROPY, LFSR, RESERVED3); + + /* SE_RNG_SRC_CONFIG */ + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_HW_DISABLE_CYA, 2, DISABLE, ENABLE); + DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_SUBSAMPLE, 4, 3); + DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_DATA_FLUSH, 8, 1); + +} diff --git a/libraries/libexosphere/source/se/se_rng.cpp b/libraries/libexosphere/source/se/se_rng.cpp new file mode 100644 index 000000000..4cb099bde --- /dev/null +++ b/libraries/libexosphere/source/se/se_rng.cpp @@ -0,0 +1,146 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constexpr inline int RngReseedInterval = 70001; + + void ConfigRng(volatile SecurityEngineRegisters *SE, SE_CONFIG_DST dst, SE_RNG_CONFIG_MODE mode) { + /* Configure the engine to do RNG encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_ENC_ALG, RNG), + SE_REG_BITS_ENUM (CONFIG_DEC_ALG, NOP), + SE_REG_BITS_VALUE(CONFIG_DST, dst)); + + reg::Write(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB), + SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_CORE_SEL, ENCRYPT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, RANDOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + /* Configure the RNG to use Entropy as source. */ + reg::Write(SE->SE_RNG_CONFIG, SE_REG_BITS_ENUM(RNG_CONFIG_SRC, ENTROPY), SE_REG_BITS_VALUE(RNG_CONFIG_MODE, mode)); + } + + } + + void InitializeRandom() { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Lock the entropy source. */ + reg::Write(SE->SE_RNG_SRC_CONFIG, SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, ENABLE), + SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, ENABLE)); + + /* Set the reseed interval to force a reseed every 70000 blocks. */ + SE->SE_RNG_RESEED_INTERVAL = RngReseedInterval; + + /* Initialize the DRBG. */ + { + u8 dummy_buf[AesBlockSize]; + + /* Configure the engine to force drbg instantiation by writing random to memory. */ + ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_FORCE_INSTANTIATION); + + /* Configure to do a single RNG block operation to trigger DRBG init. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dummy_buf, sizeof(dummy_buf), nullptr, 0); + } + } + + void GenerateRandomBytes(void *dst, size_t size) { + /* If we're not generating any bytes, there's nothing to do. */ + if (size == 0) { + return; + } + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine how many blocks to generate. */ + const size_t num_blocks = size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + const size_t fractional = size - aligned_size; + + /* Configure the RNG to generate random to memory. */ + ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_NORMAL); + + /* Generate as many aligned blocks as we can. */ + if (aligned_size > 0) { + /* Configure the engine to generate the right number of blocks. */ + SE->SE_CRYPTO_LAST_BLOCK = num_blocks - 1; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, aligned_size, nullptr, 0); + } + + /* Generate a single block to output. */ + if (fractional > 0) { + ExecuteOperationSingleBlock(SE, static_cast<u8 *>(dst) + aligned_size, fractional, nullptr, 0); + } + } + + void SetRandomKey(int slot) { + /* NOTE: Nintendo does not validate the destination keyslot here. */ + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the RNG to output to the keytable. */ + ConfigRng(SE, SE_CONFIG_DST_KEYTABLE, SE_RNG_CONFIG_MODE_NORMAL); + + /* Configure the keytable destination to be the low part of the key. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3)); + + /* Configure a single block operation. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation to generate a random low-part of the key. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + + /* Configure the keytable destination to be the high part of the key. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_4_7)); + + /* Execute the operation to generate a random high-part of the key. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + } + + void GenerateSrk() { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the RNG to output to SRK and force a reseed. */ + ConfigRng(SE, SE_CONFIG_DST_SRK, SE_RNG_CONFIG_MODE_FORCE_RESEED); + + /* Configure a single block operation. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + } + +} diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp new file mode 100644 index 000000000..d39a040dc --- /dev/null +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -0,0 +1,125 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + struct RsaKeyInfo { + int modulus_size_val; + int exponent_size_val; + }; + + constinit RsaKeyInfo g_rsa_key_infos[RsaKeySlotCount] = {}; + + void ClearRsaKeySlot(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + constexpr int NumWords = se::RsaSize / sizeof(u32); + for (int i = 0; i < NumWords; ++i) { + /* Select the keyslot word. */ + reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i)); + + /* Clear the keyslot word. */ + SE->SE_RSA_KEYTABLE_DATA = 0; + } + } + + void SetRsaKey(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + const int num_words = key_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot word. */ + reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i)); + + /* Get the word. */ + const u32 word = util::LoadBigEndian(static_cast<const u32 *>(key) + (num_words - 1 - i)); + + /* Write the keyslot word. */ + SE->SE_RSA_KEYTABLE_DATA = word; + } + } + + } + + void ClearRsaKeySlot(int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + + /* Clear the info. */ + g_rsa_key_infos[slot] = {}; + + /* Clear the modulus. */ + ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); + + /* Clear the exponent. */ + ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); + } + + void LockRsaKeySlot(int slot, u32 flags) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set non per-key flags. */ + if ((flags & ~KeySlotLockFlags_PerKey) != 0) { + /* Pack the flags into the expected format. */ + u32 value = 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 0) : 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 1) : 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 2) : 0; + + reg::Write(SE->SE_RSA_KEYTABLE_ACCESS[slot], SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYREAD, (flags & KeySlotLockFlags_KeyRead) != 0, DISABLE, ENABLE), + SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUPDATE, (flags & KeySlotLockFlags_KeyWrite) != 0, DISABLE, ENABLE), + SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUSE, (flags & KeySlotLockFlags_KeyUse) != 0, DISABLE, ENABLE)); + } + + /* Set per-key flag. */ + if ((flags & KeySlotLockFlags_PerKey) != 0) { + reg::ReadWrite(SE->SE_RSA_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0)); + } + } + + void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size) { + /* Validate the key slot and sizes. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(mod_size <= RsaSize); + AMS_ABORT_UNLESS(exp_size <= RsaSize); + + /* Set the sizes in the info. */ + auto &info = g_rsa_key_infos[slot]; + info.modulus_size_val = (mod_size / 64) - 1; + info.exponent_size_val = (exp_size / 4); + + /* Set the modulus and exponent. */ + SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); + SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + } + +} diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp new file mode 100644 index 000000000..03473deb4 --- /dev/null +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -0,0 +1,59 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + bool TestRegister(volatile u32 &r, u16 v) { + return (static_cast<u16>(reg::Read(r))) == v; + } + + } + + bool ValidateStickyBits(const StickyBits &bits) { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Check SE_SECURITY. */ + if (!TestRegister(SE->SE_SE_SECURITY, bits.se_security)) { return false; } + + /* Check TZRAM_SECURITY. */ + if (!TestRegister(SE->SE_TZRAM_SECURITY, bits.tzram_security)) { return false; } + + /* Check CRYPTO_SECURITY_PERKEY. */ + if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.crypto_security_perkey)) { return false; } + + /* Check CRYPTO_KEYTABLE_ACCESS. */ + for (int i = 0; i < AesKeySlotCount; ++i) { + if (!TestRegister(SE->SE_CRYPTO_KEYTABLE_ACCESS[i], bits.crypto_keytable_access[i])) { return false; } + } + + /* Test RSA_SCEURITY_PERKEY */ + if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.rsa_security_perkey)) { return false; } + + /* Check RSA_KEYTABLE_ACCESS. */ + for (int i = 0; i < RsaKeySlotCount; ++i) { + if (!TestRegister(SE->SE_RSA_KEYTABLE_ACCESS[i], bits.rsa_keytable_access[i])) { return false; } + } + + /* All sticky bits are valid. */ + return true; + } + +} diff --git a/libraries/libexosphere/source/tsec/tsec_api.cpp b/libraries/libexosphere/source/tsec/tsec_api.cpp new file mode 100644 index 000000000..b8994ebc4 --- /dev/null +++ b/libraries/libexosphere/source/tsec/tsec_api.cpp @@ -0,0 +1,35 @@ +/* + * 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> + +namespace ams::tsec { + + namespace { + + + + } + + void Lock() { + /* Set the tsec host1x syncpoint (160) to be secure. */ + /* TODO: constexpr value. */ + reg::ReadWrite(0x500038F8, REG_BITS_VALUE(0, 1, 0)); + + /* Clear the tsec host1x syncpoint. */ + reg::Write(0x50003300, 0); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/uart/uart_api.cpp b/libraries/libexosphere/source/uart/uart_api.cpp new file mode 100644 index 000000000..533748106 --- /dev/null +++ b/libraries/libexosphere/source/uart/uart_api.cpp @@ -0,0 +1,113 @@ +/* + * 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 "uart_registers.hpp" + +namespace ams::uart { + + namespace { + + constexpr inline const u16 UartRegisterOffsets[Port_Count] = { + secmon::MemoryRegionPhysicalDeviceUartA.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + secmon::MemoryRegionPhysicalDeviceUartB.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + secmon::MemoryRegionPhysicalDeviceUartC.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + }; + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceUart.GetAddress(); + + volatile UartRegisters *GetRegisters(Port port) { + return reinterpret_cast<volatile UartRegisters *>(g_register_address + UartRegisterOffsets[port]); + } + + void WaitSymbols(int baud, u32 num) { + util::WaitMicroSeconds(util::DivideUp(1'000'000, baud) * num); + } + + void WaitCycles(int baud, u32 num) { + util::WaitMicroSeconds(util::DivideUp(1'000'000, 16 * baud) * num); + } + + ALWAYS_INLINE void WaitFifoNotFull(volatile UartRegisters *uart) { + while ((uart->lsr & UART_LSR_TX_FIFO_FULL) != 0) { /* ... */ } + } + + ALWAYS_INLINE void WaitFifoNotEmpty(volatile UartRegisters *uart) { + while ((uart->lsr & UART_LSR_RX_FIFO_EMPTY) != 0) { /* ... */ } + } + + void WaitIdle(volatile UartRegisters *uart, u32 vendor_state) { + if (vendor_state & UART_VENDOR_STATE_TX_IDLE) { + while ((uart->lsr & UART_LSR_TMTY) == 0) { /* ... */ } + } + + if (vendor_state & UART_VENDOR_STATE_RX_IDLE) { + while ((uart->lsr & UART_LSR_RDR) != 0) { /* ... */ } + } + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void Initialize(Port port, int baud_rate, u32 flags) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Parse flags. */ + const bool inverted = (flags & Flag_Inverted) != 0; + + /* Calculate the baud rate divisor. */ + constexpr u32 UartClock = 408000000; + const u32 divisor = (UartClock + (baud_rate * 16) / 2) / (baud_rate * 16); + + /* Disable DLAB and all interrupts. */ + uart->lcr = uart->lcr & ~UART_LCR_DLAB; + uart->ier = 0; + uart->mcr = 0; + + /* Setup the uart in FIFO mode. */ + uart->lcr = UART_LCR_DLAB | UART_LCR_WD_LENGTH_8; + uart->dll = static_cast<u8>(divisor); + uart->dlh = static_cast<u8>(divisor >> 8); + uart->lcr = uart->lcr & ~UART_LCR_DLAB; + reg::Read(std::addressof(uart->spr)); + + /* Wait three symbols. */ + WaitSymbols(baud_rate, 3); + + /* Enable FIFO with default settings. */ + uart->fcr = UART_FCR_FCR_EN_FIFO; + uart->irda_csr = inverted ? UART_IRDA_CSR_INVERT_TXD : 0; + reg::Read(std::addressof(uart->spr)); + + /* Wait three cycles. */ + WaitCycles(baud_rate, 3); + + /* Flush the FIFO. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE); + uart->fcr = uart->fcr | UART_FCR_RX_CLR | UART_FCR_TX_CLR; + WaitCycles(baud_rate, 32); + + /* Wait for idle state. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE | UART_VENDOR_STATE_RX_IDLE); + + /* Set scratch register to 0. */ + uart->spr = 0; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/uart/uart_registers.hpp b/libraries/libexosphere/source/uart/uart_registers.hpp new file mode 100644 index 000000000..509de989f --- /dev/null +++ b/libraries/libexosphere/source/uart/uart_registers.hpp @@ -0,0 +1,180 @@ +/* + * 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> + +namespace ams::uart { + + struct UartRegisters { + union { + u32 thr; + u32 rbr; + u32 dll; + }; + union { + u32 ier; + u32 dlh; + }; + union { + u32 iir; + u32 fcr; + }; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 spr; + u32 irda_csr; + u32 rx_fifo_cfg; + u32 mie; + u32 vendor_status; + u32 reserved_30; + u32 reserved_34; + u32 reserved_38; + u32 asr; + }; + static_assert(util::is_pod<UartRegisters>::value); + static_assert(sizeof(UartRegisters) == 0x40); + + /* 36.3.12 UART_VENDOR_STATUS_0_0 */ + enum UartVendorStatus { + UART_VENDOR_STATE_TX_IDLE = 1 << 0, + UART_VENDOR_STATE_RX_IDLE = 1 << 1, + + /* This bit is set to 1 when a read is issued to an empty FIFO and gets cleared on register read (sticky bit until read) + 0 = NO_UNDERRUN + 1 = UNDERRUN + */ + UART_VENDOR_STATE_RX_UNDERRUN = 1 << 2, + + /* This bit is set to 1 when write data is issued to the TX FIFO when it is already full and gets cleared on register read (sticky bit until read) + 0 = NO_OVERRUN + 1 = OVERRUN + */ + UART_VENDOR_STATE_TX_OVERRUN = 1 << 3, + + UART_VENDOR_STATE_RX_FIFO_COUNTER = 0b111111 << 16, /* reflects number of current entries in RX FIFO */ + UART_VENDOR_STATE_TX_FIFO_COUNTER = 0b111111 << 24 /* reflects number of current entries in TX FIFO */ + }; + + /* 36.3.6 UART_LSR_0 */ + enum UartLineStatus { + UART_LSR_RDR = 1 << 0, /* Receiver Data Ready */ + UART_LSR_OVRF = 1 << 1, /* Receiver Overrun Error */ + UART_LSR_PERR = 1 << 2, /* Parity Error */ + UART_LSR_FERR = 1 << 3, /* Framing Error */ + UART_LSR_BRK = 1 << 4, /* BREAK condition detected on line */ + UART_LSR_THRE = 1 << 5, /* Transmit Holding Register is Empty -- OK to write data */ + UART_LSR_TMTY = 1 << 6, /* Transmit Shift Register empty status */ + UART_LSR_FIFOE = 1 << 7, /* Receive FIFO Error */ + UART_LSR_TX_FIFO_FULL = 1 << 8, /* Transmitter FIFO full status */ + UART_LSR_RX_FIFO_EMPTY = 1 << 9, /* Receiver FIFO empty status */ + }; + + /* 36.3.4 UART_LCR_0 */ + enum UartLineControl { + UART_LCR_WD_LENGTH_5 = 0, /* word length 5 */ + UART_LCR_WD_LENGTH_6 = 1, /* word length 6 */ + UART_LCR_WD_LENGTH_7 = 2, /* word length 7 */ + UART_LCR_WD_LENGTH_8 = 3, /* word length 8 */ + + /* STOP: + 0 = Transmit 1 stop bit + 1 = Transmit 2 stop bits (receiver always checks for 1 stop bit) + */ + UART_LCR_STOP = 1 << 2, + UART_LCR_PAR = 1 << 3, /* Parity enabled */ + UART_LCR_EVEN = 1 << 4, /* Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1) */ + UART_LCR_SET_P = 1 << 5, /* Set (force) parity to value in LCR[4] */ + UART_LCR_SET_B = 1 << 6, /* Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK */ + UART_LCR_DLAB = 1 << 7, /* Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors) */ + }; + + /* 36.3.3 UART_IIR_FCR_0 */ + enum UartFifoControl { + UART_FCR_FCR_EN_FIFO = 1 << 0, /* Enable the transmit and receive FIFOs. This bit should be enabled */ + UART_FCR_RX_CLR = 1 << 1, /* Clears the contents of the receive FIFO and resets its counter logic to 0 (the receive shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + UART_FCR_TX_CLR = 1 << 2, /* Clears the contents of the transmit FIFO and resets its counter logic to 0 (the transmit shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + + /* DMA: + 0 = DMA_MODE_0 + 1 = DMA_MODE_1 + */ + UART_FCR_DMA = 1 << 3, + + /* TX_TRIG + 0 = FIFO_COUNT_GREATER_16 + 1 = FIFO_COUNT_GREATER_8 + 2 = FIFO_COUNT_GREATER_4 + 3 = FIFO_COUNT_GREATER_1 + */ + UART_FCR_TX_TRIG = 3 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4, + + /* RX_TRIG + 0 = FIFO_COUNT_GREATER_1 + 1 = FIFO_COUNT_GREATER_4 + 2 = FIFO_COUNT_GREATER_8 + 3 = FIFO_COUNT_GREATER_16 + */ + UART_FCR_RX_TRIG = 3 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6, + }; + + /* 36.3.2 UART_IER_DLAB_0_0 */ + enum UartInterruptEnable { + UART_IER_IE_RHR = 1 << 0, /* Interrupt enable for Received Data Interrupt */ + UART_IER_IE_THR = 1 << 1, /* Interrupt enable for Transmitter Holding Register Empty interrupt */ + UART_IER_IE_RXS = 1 << 2, /* Interrupt enable for Receiver Line Status Interrupt */ + UART_IER_IE_MSI = 1 << 3, /* Interrupt enable for Modem Status Interrupt */ + UART_IER_IE_RX_TIMEOUT = 1 << 4, /* Interrupt enable for RX FIFO timeout */ + UART_IER_IE_EORD = 1 << 5, /* Interrupt enable for Interrupt Enable for End of Received Data */ + }; + + /* 36.3.3 UART_IIR_FCR_0 */ + enum UartInterruptIdentification { + UART_IIR_IS_STA = 1 << 0, /* Interrupt Pending if ZERO */ + UART_IIR_IS_PRI0 = 1 << 1, /* Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3] */ + UART_IIR_IS_PRI1 = 1 << 2, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + UART_IIR_IS_PRI2 = 1 << 3, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + + /* FIFO Mode Status + 0 = 16450 mode (no FIFO) + 1 = 16550 mode (FIFO) + */ + UART_IIR_EN_FIFO = 3 << 6, + UART_IIR_MODE_16450 = 0 << 6, + UART_IIR_MODE_16550 = 1 << 6, + }; + + /* 36.3.9 UART_IRDA_CSR_0 */ + enum UartIrDAPulseCodingCSR { + UART_IRDA_CSR_INVERT_RXD = 1 << 0, + UART_IRDA_CSR_INVERT_TXD = 1 << 1, + UART_IRDA_CSR_INVERT_CTS = 1 << 2, + UART_IRDA_CSR_INVERT_RTS = 1 << 3, + + UART_IRDA_CSR_PWT_A_BAUD_PULSE_3_14 = 0 << 6, + UART_IRDA_CSR_PWT_A_BAUD_PULSE_4_14 = 1 << 6, + UART_IRDA_CSR_SIR_A = 1 << 7, + }; + +} diff --git a/libraries/libexosphere/source/util/util_api.cpp b/libraries/libexosphere/source/util/util_api.cpp new file mode 100644 index 000000000..c8d46f554 --- /dev/null +++ b/libraries/libexosphere/source/util/util_api.cpp @@ -0,0 +1,50 @@ +/* + * 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> + +namespace ams::util { + + namespace { + + constinit uintptr_t g_timer_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + ALWAYS_INLINE uintptr_t GetCurrentTimeRegisterAddress() { + return g_timer_register_address + 0x10; + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_timer_register_address = address; + } + + u32 GetMicroSeconds() { + return reg::Read(GetCurrentTimeRegisterAddress()); + } + + void WaitMicroSeconds(int us) { + const u32 start = reg::Read(GetCurrentTimeRegisterAddress()); + u32 cur = start; + while ((cur - start) <= static_cast<u32>(us)) { + cur = reg::Read(GetCurrentTimeRegisterAddress()); + } + } + + void ClearMemory(void *ptr, size_t size) { + std::memset(ptr, 0, size); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/wdt/wdt_api.cpp b/libraries/libexosphere/source/wdt/wdt_api.cpp new file mode 100644 index 000000000..cb26f5e05 --- /dev/null +++ b/libraries/libexosphere/source/wdt/wdt_api.cpp @@ -0,0 +1,99 @@ +/* + * 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> + +namespace ams::wdt { + + namespace { + + volatile uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + #if defined(ATMOSPHERE_ARCH_ARM64) + NOINLINE void Reboot(uintptr_t registers) { + __asm__ __volatile__( + /* Get the current core. */ + "mrs x12, mpidr_el1\n" + "and x12, x12, #0xFF\n" + + /* Get the offsets of the registers we want to write */ + "mov x10, #0x8\n" + "mov x11, #0x20\n" + "madd x10, x10, x12, %[registers]\n" + "madd x11, x11, x12, %[registers]\n" + "add x10, x10, #0x60\n" + "add x11, x11, #0x100\n" + + /* Write the magic unlock pattern. */ + "mov w9, #0xC45A\n" + "str w9, [x11, #0xC]\n" + + /* Disable the counters. */ + "mov w9, #0x2\n" + "str w9, [x11, #0x8]\n" + + /* Start periodic timer. */ + "mov w9, #0xC0000000\n" + "str w9, [x10]\n" + + /* Set reboot source to the timer we started. */ + "mov w9, #0x8015\n" + "add w9, w9, w12\n" + "str w9, [x11]\n" + + /* Enable the counters. */ + "mov w9, #0x1\n" + "str w9, [x11, #0x8]\n" + + /* Wait forever. */ + "1: b 1b" + : [registers]"=&r"(registers) + : + : "x9", "x10", "x11", "x12", "memory" + ); + } + #elif defined(ATMOSPHERE_ARCH_ARM) + NOINLINE void Reboot(uintptr_t registers) { + /* Write the magic unlock pattern. */ + reg::Write(registers + 0x18C, 0xC45A); + + /* Disable the counters. */ + reg::Write(registers + 0x188, 0x2); + + /* Start periodic timer. */ + reg::Write(registers + 0x080, 0xC0000000); + + /* Set reboot source to the timer we started. */ + reg::Write(registers + 0x180, 0x8019); + + /* Enable the counters. */ + reg::Write(registers + 0x188, 0x1); + + while (true) { /* ... */ } + } + #endif + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + NOINLINE void Reboot() { + const uintptr_t registers = g_register_address; + Reboot(registers); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp index 905d479a3..4108343ab 100644 --- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp +++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp @@ -132,4 +132,8 @@ namespace ams::diag { AbortWithValue(value); } + NORETURN WEAK_SYMBOL void AbortImpl() { + AbortWithValue(0); + } + } diff --git a/libraries/libvapours/include/vapours/assert.hpp b/libraries/libvapours/include/vapours/assert.hpp index 8402cbc52..8cd61bd61 100644 --- a/libraries/libvapours/include/vapours/assert.hpp +++ b/libraries/libvapours/include/vapours/assert.hpp @@ -32,6 +32,7 @@ namespace ams::diag { NORETURN NOINLINE void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7))); NORETURN NOINLINE void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value); + NORETURN NOINLINE void AbortImpl(); } @@ -42,7 +43,7 @@ namespace ams::diag { #define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__) #else #define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl("", 0, "", "", 0) -#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl("", 0, "", "", 0) +#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl() #endif #ifdef AMS_ENABLE_ASSERTIONS diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp index b588bc2c3..df5955d68 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp @@ -142,7 +142,7 @@ namespace ams::crypto::impl { u64 _block[IvSize / sizeof(u64)] = {}; util::StoreBigEndian(std::addressof(_block[(IvSize / sizeof(u64)) - 1]), count); - u16 acc; + u16 acc = 0; const u8 *block = reinterpret_cast<const u8 *>(_block); for (s32 i = IvSize - 1; i >= 0; --i) { acc += (this->counter[i] + block[i]); diff --git a/libraries/libvapours/include/vapours/defines.hpp b/libraries/libvapours/include/vapours/defines.hpp index 391640806..61c733237 100644 --- a/libraries/libvapours/include/vapours/defines.hpp +++ b/libraries/libvapours/include/vapours/defines.hpp @@ -59,4 +59,6 @@ #define AMS_LIKELY(expr) AMS_PREDICT_TRUE(expr, 1.0) #define AMS_UNLIKELY(expr) AMS_PREDICT_FALSE(expr, 1.0) +#define AMS_ASSUME(expr) do { if (!static_cast<bool>((expr))) { __builtin_unreachable(); } } while (0) + #define AMS_CURRENT_FUNCTION_NAME __FUNCTION__ diff --git a/libraries/libvapours/include/vapours/literals.hpp b/libraries/libvapours/include/vapours/literals.hpp index ddd22a6b6..7f741a916 100644 --- a/libraries/libvapours/include/vapours/literals.hpp +++ b/libraries/libvapours/include/vapours/literals.hpp @@ -19,16 +19,16 @@ namespace ams { inline namespace literals { - constexpr ALWAYS_INLINE size_t operator ""_KB(unsigned long long n) { - return static_cast<size_t>(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_KB(unsigned long long n) { + return static_cast<u64>(n) * UINT64_C(1024); } - constexpr ALWAYS_INLINE size_t operator ""_MB(unsigned long long n) { - return operator ""_KB(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_MB(unsigned long long n) { + return operator ""_KB(n) * UINT64_C(1024); } - constexpr ALWAYS_INLINE size_t operator ""_GB(unsigned long long n) { - return operator ""_MB(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_GB(unsigned long long n) { + return operator ""_MB(n) * UINT64_C(1024); } } } diff --git a/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp new file mode 100644 index 000000000..391dad000 --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp @@ -0,0 +1,38 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours/svc/svc_types_common.hpp> + +namespace ams::svc::arch::arm { + + constexpr inline size_t NumTlsSlots = 16; + constexpr inline size_t MessageBufferSize = 0x100; + + struct ThreadLocalRegion { + u32 message_buffer[MessageBufferSize / sizeof(u32)]; + volatile u16 disable_count; + volatile u16 interrupt_flag; + /* TODO: Should we bother adding the Nintendo aarch32 thread local context here? */ + uintptr_t TODO[(0x200 - 0x104) / sizeof(uintptr_t)]; + }; + + ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() { + ThreadLocalRegion *tlr; + __asm__ __volatile__("mrc p15, 0, %[tlr], c13, c0, 3" : [tlr]"=&r"(tlr) :: "memory"); + return tlr; + } + +} diff --git a/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp index 3f3393661..e7b3e6f60 100644 --- a/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp @@ -25,6 +25,14 @@ using ams::svc::arch::arm64::GetThreadLocalRegion; } +#elif defined(ATMOSPHERE_ARCH_ARM) + + #include <vapours/svc/arch/arm/svc_thread_local_region.hpp> + namespace ams::svc { + using ams::svc::arch::arm::ThreadLocalRegion; + using ams::svc::arch::arm::GetThreadLocalRegion; + } + #else #error "Unknown architecture for svc::ThreadLocalRegion" diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 4567e5d0b..8e7a5049e 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -225,7 +225,7 @@ namespace ams::svc { /* Thread types. */ using ThreadFunc = ams::svc::Address; -#ifdef ATMOSPHERE_ARCH_ARM64 +#if defined(ATMOSPHERE_ARCH_ARM64) struct ThreadContext { u64 r[29]; @@ -242,8 +242,23 @@ namespace ams::svc { }; static_assert(sizeof(ThreadContext) == 0x320); +#elif defined(ATMOSPHERE_ARCH_ARM) + + struct ThreadContext { + u32 r[13]; + u32 sp; + u32 lr; + u32 pc; + u32 cpsr; + u32 padding; + u64 fpu_registers[32]; + u32 fpscr; + u32 fpexc; + u32 tpidr; + }; + #else - #error >Unknown Architecture for ams::svc::ThreadContext> + #error "Unknown Architecture for ams::svc::ThreadContext" #endif enum ThreadSuspend : u32 { diff --git a/libraries/libvapours/include/vapours/types.hpp b/libraries/libvapours/include/vapours/types.hpp index d77ea21fe..6807e9df2 100644 --- a/libraries/libvapours/include/vapours/types.hpp +++ b/libraries/libvapours/include/vapours/types.hpp @@ -25,25 +25,28 @@ typedef uint8_t u8; ///< 8-bit unsigned integer. typedef uint16_t u16; ///< 16-bit unsigned integer. typedef uint32_t u32; ///< 32-bit unsigned integer. typedef uint64_t u64; ///< 64-bit unsigned integer. -typedef __uint128_t u128; ///< 128-bit unsigned integer. typedef int8_t s8; ///< 8-bit signed integer. typedef int16_t s16; ///< 16-bit signed integer. typedef int32_t s32; ///< 32-bit signed integer. typedef int64_t s64; ///< 64-bit signed integer. -typedef __int128_t s128; ///< 128-bit unsigned integer. typedef volatile u8 vu8; ///< 8-bit volatile unsigned integer. typedef volatile u16 vu16; ///< 16-bit volatile unsigned integer. typedef volatile u32 vu32; ///< 32-bit volatile unsigned integer. typedef volatile u64 vu64; ///< 64-bit volatile unsigned integer. -typedef volatile u128 vu128; ///< 128-bit volatile unsigned integer. typedef volatile s8 vs8; ///< 8-bit volatile signed integer. typedef volatile s16 vs16; ///< 16-bit volatile signed integer. typedef volatile s32 vs32; ///< 32-bit volatile signed integer. typedef volatile s64 vs64; ///< 64-bit volatile signed integer. + +#ifdef ATMOSPHERE_ARCH_ARM64 +typedef __uint128_t u128; ///< 128-bit unsigned integer. +typedef __int128_t s128; ///< 128-bit unsigned integer. +typedef volatile u128 vu128; ///< 128-bit volatile unsigned integer. typedef volatile s128 vs128; ///< 128-bit volatile signed integer. +#endif typedef u32 Result; ///< Function error code result type. diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 43e2806e1..c10fa5d37 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -21,6 +21,7 @@ #include <vapours/util/util_type_traits.hpp> #include <vapours/util/util_alignment.hpp> #include <vapours/util/util_size.hpp> +#include <vapours/util/util_aligned_buffer.hpp> #include <vapours/util/util_endian.hpp> #include <vapours/util/util_scope_guard.hpp> #include <vapours/util/util_specialization_of.hpp> diff --git a/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp b/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp new file mode 100644 index 000000000..fc405490e --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp @@ -0,0 +1,35 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util/util_alignment.hpp> + +namespace ams::util { + + template<size_t Alignment, size_t Size> + class AlignedBuffer { + private: + static constexpr size_t AlignedSize = ((Size + Alignment - 1) / Alignment) * Alignment; + static_assert(AlignedSize % Alignment == 0); + private: + u8 buffer[Alignment + AlignedSize]; + public: + ALWAYS_INLINE operator u8 *() { return reinterpret_cast<u8 *>(util::AlignUp(reinterpret_cast<uintptr_t>(this->buffer), Alignment)); } + }; + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/util/util_bitpack.hpp b/libraries/libvapours/include/vapours/util/util_bitpack.hpp index c89058d61..4b5c1e863 100644 --- a/libraries/libvapours/include/vapours/util/util_bitpack.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitpack.hpp @@ -42,7 +42,7 @@ namespace ams::util { } }(); public: - template<size_t _Index, size_t _Count, typename T> + template<size_t _Index, size_t _Count, typename T = IntegralStorageType> struct Field { using Type = T; static constexpr size_t Index = _Index; diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp index 19844f202..f269796d2 100644 --- a/libraries/libvapours/include/vapours/util/util_endian.hpp +++ b/libraries/libvapours/include/vapours/util/util_endian.hpp @@ -135,21 +135,21 @@ namespace ams::util { } template<typename T> requires std::integral<T> - constexpr ALWAYS_INLINE T LoadBigEndian(T *ptr) { + constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) { return ConvertToBigEndian(*ptr); } template<typename T> requires std::integral<T> - constexpr ALWAYS_INLINE T LoadLittleEndian(T *ptr) { + constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) { return ConvertToLittleEndian(*ptr); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) { *ptr = ConvertToBigEndian(val); } - template<typename T> + template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) { *ptr = ConvertToLittleEndian(val); } diff --git a/libraries/libvapours/include/vapours/util/util_tinymt.hpp b/libraries/libvapours/include/vapours/util/util_tinymt.hpp index 7611c9b7c..a200fc4b2 100644 --- a/libraries/libvapours/include/vapours/util/util_tinymt.hpp +++ b/libraries/libvapours/include/vapours/util/util_tinymt.hpp @@ -242,7 +242,7 @@ namespace ams::util { const u32 first = (this->GenerateRandomU32() >> Shift1st); const u32 second = (this->GenerateRandomU32() >> Shift2nd); - return (1.0 * first * (1ul << (32 - Shift2nd)) + second) * (1.0 / (1ul << MantissaBits)); + return (1.0 * first * (static_cast<u64>(1) << (32 - Shift2nd)) + second) * (1.0 / (static_cast<u64>(1) << MantissaBits)); } }; From cbcd1d87fb1c9ee3223356daa5e272c5a02ec6f6 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 11 May 2020 18:54:35 -0700 Subject: [PATCH 042/118] exo2: implement through end of random cache init --- .../program/source/boot/secmon_main.cpp | 7 +- exosphere2/program/source/secmon_setup.cpp | 123 ++++++++++++++++++ exosphere2/program/source/secmon_setup.hpp | 2 + .../program/source/secmon_setup_warm.cpp | 2 +- .../source/smc/secmon_random_cache.cpp | 103 +++++++++++++++ .../source/smc/secmon_random_cache.hpp | 26 ++++ libraries/libexosphere/include/exosphere.hpp | 1 + .../libexosphere/include/exosphere/flow.hpp | 25 ++++ .../hw/hw_arm64_system_registers.hpp | 103 +++++++++++++-- .../exosphere/tegra/tegra_flow_ctlr.hpp | 15 +++ .../libexosphere/source/flow/flow_api.cpp | 51 ++++++++ 11 files changed, 447 insertions(+), 11 deletions(-) create mode 100644 exosphere2/program/source/smc/secmon_random_cache.cpp create mode 100644 exosphere2/program/source/smc/secmon_random_cache.hpp create mode 100644 libraries/libexosphere/include/exosphere/flow.hpp create mode 100644 libraries/libexosphere/source/flow/flow_api.cpp diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 738452dab..9818f7ef7 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -16,6 +16,7 @@ #include <exosphere.hpp> #include "secmon_boot.hpp" #include "secmon_boot_functions.hpp" +#include "../smc/secmon_random_cache.hpp" #include "../secmon_setup.hpp" #include "../secmon_misc.hpp" @@ -56,13 +57,17 @@ namespace ams::secmon { /* Setup the SoC security measures. */ secmon::SetupSocSecurity(); - /* TODO: More init. */ + /* Setup the Cpu core context. */ + secmon::SetupCpuCoreContext(); /* Clear the crt0 code that was present in iram. */ secmon::boot::ClearIram(); /* Alert the bootloader that we're initialized. */ secmon_params.secmon_state = pkg1::SecureMonitorState_Initialized; + + /* Initialize the random cache. */ + secmon::smc::FillRandomCache(); } } diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index a468d6dce..7bb95aa63 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -629,6 +629,99 @@ namespace ams::secmon { reg::Read (MC + MC_SMMU_TLB_CONFIG); } + void SetupSecureEl2AndEl1SystemRegisters() { + /* Setup actlr_el2 and actlr_el3. */ + { + util::BitPack32 actlr = {}; + + actlr.Set<hw::ActlrCortexA57::Cpuactlr>(1); /* Enable access to cpuactlr from lower EL. */ + actlr.Set<hw::ActlrCortexA57::Cpuectlr>(1); /* Enable access to cpuectlr from lower EL. */ + actlr.Set<hw::ActlrCortexA57::L2ctlr>(1); /* Enable access to l2ctlr from lower EL. */ + actlr.Set<hw::ActlrCortexA57::L2actlr>(1); /* Enable access to l2actlr from lower EL. */ + actlr.Set<hw::ActlrCortexA57::L2ectlr>(1); /* Enable access to l2ectlr from lower EL. */ + + HW_CPU_SET_ACTLR_EL3(actlr); + HW_CPU_SET_ACTLR_EL2(actlr); + } + + /* Setup hcr_el2. */ + { + util::BitPack64 hcr = {}; + + hcr.Set<hw::HcrEl2::Rw>(1); /* EL1 is aarch64 mode. */ + + HW_CPU_SET_HCR_EL2(hcr); + } + + /* Configure all domain access permissions as manager. */ + HW_CPU_SET_DACR32_EL2(~0u); + + /* Setup sctlr_el1. */ + { + util::BitPack64 sctlr = { hw::SctlrEl1::Res1 }; + + sctlr.Set<hw::SctlrEl1::M>(0); /* Globally disable the MMU. */ + sctlr.Set<hw::SctlrEl1::A>(0); /* Disable alignment fault checking. */ + sctlr.Set<hw::SctlrEl1::C>(0); /* Globally disable the data and unified caches. */ + sctlr.Set<hw::SctlrEl1::Sa>(1); /* Enable stack alignment checking. */ + sctlr.Set<hw::SctlrEl1::Sa0>(1); /* Enable el0 stack alignment checking. */ + sctlr.Set<hw::SctlrEl1::Cp15BEn>(1); /* Enable cp15 barrier operations. */ + sctlr.Set<hw::SctlrEl1::Thee>(0); /* Disable ThumbEE. */ + sctlr.Set<hw::SctlrEl1::Itd>(0); /* Enable itd instructions. */ + sctlr.Set<hw::SctlrEl1::Sed>(0); /* Enable setend instruction. */ + sctlr.Set<hw::SctlrEl1::Uma>(0); /* Disable el0 interrupt mask access. */ + sctlr.Set<hw::SctlrEl1::I>(0); /* Globally disable the instruction cache. */ + sctlr.Set<hw::SctlrEl1::Dze>(0); /* Disable el0 access to dc zva instruction. */ + sctlr.Set<hw::SctlrEl1::Ntwi>(1); /* wfi instructions in el0 trap. */ + sctlr.Set<hw::SctlrEl1::Ntwe>(1); /* wfe instructions in el0 trap. */ + sctlr.Set<hw::SctlrEl1::Wxn>(0); /* Do not force writable pages to be ExecuteNever. */ + sctlr.Set<hw::SctlrEl1::E0e>(0); /* Data accesses in el0 are little endian. */ + sctlr.Set<hw::SctlrEl1::Ee>(0); /* Exceptions should be little endian. */ + sctlr.Set<hw::SctlrEl1::Uci>(0); /* Disable el0 access to dc cvau, dc civac, dc cvac, ic ivau. */ + + HW_CPU_SET_SCTLR_EL1(sctlr); + } + + /* Setup sctlr_el2. */ + { + util::BitPack64 sctlr = { hw::SctlrEl2::Res1 }; // 0x30C5083 + + sctlr.Set<hw::SctlrEl2::M>(0); /* Globally disable the MMU. */ + sctlr.Set<hw::SctlrEl2::A>(0); /* Disable alignment fault checking. */ + sctlr.Set<hw::SctlrEl2::C>(0); /* Globally disable the data and unified caches. */ + sctlr.Set<hw::SctlrEl2::Sa>(1); /* Enable stack alignment checking. */ + sctlr.Set<hw::SctlrEl2::I>(0); /* Globally disable the instruction cache. */ + sctlr.Set<hw::SctlrEl2::Wxn>(0); /* Do not force writable pages to be ExecuteNever. */ + sctlr.Set<hw::SctlrEl2::Ee>(0); /* Exceptions should be little endian. */ + + HW_CPU_SET_SCTLR_EL2(sctlr); + } + + /* Ensure instruction consistency. */ + hw::InstructionSynchronizationBarrier(); + } + + void SetupNonSecureSystemRegisters(u32 tsc_frequency) { + /* Set cntfrq_el0. */ + HW_CPU_SET_CNTFRQ_EL0(tsc_frequency); + + /* Set cnthctl_el2. */ + { + util::BitPack32 cnthctl = {}; + + cnthctl.Set<hw::CnthctlEl2::El1PctEn>(1); /* Do not trap accesses to cntpct_el0. */ + cnthctl.Set<hw::CnthctlEl2::El1PcEn>(1); /* Do not trap accesses to cntp_ctl_el0, cntp_cval_el0, and cntp_tval_el0. */ + cnthctl.Set<hw::CnthctlEl2::EvntEn>(0); /* Disable the event stream. */ + cnthctl.Set<hw::CnthctlEl2::EvntDir>(0); /* Trigger events on 0 -> 1 transition. */ + cnthctl.Set<hw::CnthctlEl2::EvntI>(0); /* Select bit0 of cntpct_el0 as the event stream trigger. */ + + HW_CPU_SET_CNTHCTL_EL2(cnthctl); + } + + /* Ensure instruction consistency. */ + hw::InstructionSynchronizationBarrier(); + } + } void Setup1() { @@ -747,4 +840,34 @@ namespace ams::secmon { } + void SetupCpuCoreContext() { + /* Get the tsc frequency. */ + const u32 tsc_frequency = reg::Read(MemoryRegionVirtualDeviceSysCtr0.GetAddress() + SYSCTR0_CNTFID0); + + /* Setup the secure EL2/EL1 system registers. */ + SetupSecureEl2AndEl1SystemRegisters(); + + /* Setup the non-secure system registers. */ + SetupNonSecureSystemRegisters(tsc_frequency); + + /* Reset the cpu flow controller registers. */ + flow::ResetCpuRegisters(hw::GetCurrentCoreId()); + + /* Initialize the core unique gic registers. */ + gic::InitializeCoreUnique(); + + /* Configure cpu fiq. */ + constexpr int FiqInterruptId = 28; + gic::SetPriority (FiqInterruptId, gic::HighestPriority); + gic::SetInterruptGroup(FiqInterruptId, 0); + gic::SetEnable (FiqInterruptId, true); + + /* Restore the cpu's debug registers. */ + RestoreDebugRegisters(); + } + + void SetupCpuSErrorDebug() { + + } + } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp index 4113c7905..623e10e74 100644 --- a/exosphere2/program/source/secmon_setup.hpp +++ b/exosphere2/program/source/secmon_setup.hpp @@ -24,6 +24,8 @@ namespace ams::secmon { constexpr inline int KernelCarveoutCount = 2; void SetupCpuMemoryControllersEnableMmu(); + void SetupCpuCoreContext(); + void SetupCpuSErrorDebug(); void SetupSocDmaControllers(); void SetupSocSecurity(); diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp index 1f168d89a..e0b661d3b 100644 --- a/exosphere2/program/source/secmon_setup_warm.cpp +++ b/exosphere2/program/source/secmon_setup_warm.cpp @@ -122,7 +122,7 @@ namespace ams::secmon { void EnableMmu() { /* Create sctlr value. */ - util::BitPack32 sctlr = { hw::SctlrEl3::Res1 }; + util::BitPack64 sctlr = { hw::SctlrEl3::Res1 }; sctlr.Set<hw::SctlrEl3::M>(1); /* Globally enable the MMU. */ sctlr.Set<hw::SctlrEl3::A>(0); /* Disable alignment fault checking. */ sctlr.Set<hw::SctlrEl3::C>(1); /* Globally enable the data and unified caches. */ diff --git a/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere2/program/source/smc/secmon_random_cache.cpp new file mode 100644 index 000000000..bcbed1df7 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_random_cache.cpp @@ -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_smc_common.hpp" +#include "secmon_random_cache.hpp" + +namespace ams::secmon::smc { + + namespace { + + constexpr inline size_t MaxRandomBytes = sizeof(SmcArguments) - sizeof(SmcArguments{}.r[0]); + + constinit int g_random_offset_low = 0; + constinit int g_random_offset_high = 0; + + void FillRandomCache(int offset, int size) { + /* Get the cache. */ + u8 * const random_cache_loc = GetRandomBytesCache() + offset; + + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(random_cache_loc, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + se::GenerateRandomBytes(random_cache_loc, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(random_cache_loc, size); + hw::DataSynchronizationBarrierInnerShareable(); + } + + } + + void FillRandomCache() { + /* Fill the cache. */ + FillRandomCache(0, GetRandomBytesCacheSize()); + + /* Set the extents. */ + g_random_offset_low = 0; + g_random_offset_high = GetRandomBytesCacheSize() - 1; + } + + void RefillRandomCache() { + /* Check that we need to do any refilling. */ + if (const int used_start = (g_random_offset_high + 1) % GetRandomBytesCacheSize(); used_start != g_random_offset_low) { + if (used_start < g_random_offset_low) { + /* The region we need to fill is after used_start but before g_random_offset_low. */ + const auto size = g_random_offset_low - used_start; + FillRandomCache(used_start, size); + g_random_offset_high += size; + } else { + /* We need to fill the space from high to the end and from low to start. */ + const int high_size = GetRandomBytesCacheSize() - used_start; + if (high_size > 0) { + FillRandomCache(used_start, high_size); + g_random_offset_high += high_size; + } + + const int low_size = g_random_offset_low; + if (low_size > 0) { + FillRandomCache(0, low_size); + g_random_offset_high += low_size; + } + + } + + g_random_offset_high %= GetRandomBytesCacheSize(); + } + } + + void GetRandomFromCache(void *dst, size_t size) { + u8 * const cache = GetRandomBytesCache(); + u8 * cur_dst = static_cast<u8 *>(dst); + + /* NOTE: Nintendo does not do bounds checking here, and does not do multiple reads when the get would wrap around. */ + while (size > 0) { + const size_t copy_size = std::min(size, static_cast<size_t>(GetRandomBytesCacheSize() - g_random_offset_low)); + std::memcpy(cur_dst, cache + g_random_offset_low, copy_size); + + cur_dst += copy_size; + size -= copy_size; + + if (g_random_offset_low + copy_size >= GetRandomBytesCacheSize()) { + g_random_offset_low = 0; + } + } + } + +} diff --git a/exosphere2/program/source/smc/secmon_random_cache.hpp b/exosphere2/program/source/smc/secmon_random_cache.hpp new file mode 100644 index 000000000..f3cbdc731 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_random_cache.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + void FillRandomCache(); + void RefillRandomCache(); + void GetRandomFromCache(void *dst, size_t size); + +} diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index db9b2cb2d..c92a382d9 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -26,6 +26,7 @@ #include <exosphere/pkg1.hpp> #include <exosphere/tsec.hpp> #include <exosphere/se.hpp> +#include <exosphere/flow.hpp> #include <exosphere/fuse.hpp> #include <exosphere/i2c.hpp> #include <exosphere/uart.hpp> diff --git a/libraries/libexosphere/include/exosphere/flow.hpp b/libraries/libexosphere/include/exosphere/flow.hpp new file mode 100644 index 000000000..93cfb98c3 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/flow.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::flow { + + void SetRegisterAddress(uintptr_t address); + + void ResetCpuRegisters(int core); + +} diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp index 637f3c026..5792ee713 100644 --- a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp @@ -141,17 +141,79 @@ namespace ams::hw::arch::arm64 { #define HW_CPU_GET_DBGWCR3_EL1(value) HW_CPU_GET_SYSREG(dbgwcr3_el1, value) #define HW_CPU_SET_DBGWCR3_EL1(value) HW_CPU_SET_SYSREG(dbgwcr3_el1, value) + #define HW_CPU_GET_CNTFRQ_EL0(value) HW_CPU_GET_SYSREG(cntfrq_el0, value) + #define HW_CPU_SET_CNTFRQ_EL0(value) HW_CPU_SET_SYSREG(cntfrq_el0, value) + + #define HW_CPU_GET_CNTHCTL_EL2(value) HW_CPU_GET_SYSREG(cnthctl_el2, value) + #define HW_CPU_SET_CNTHCTL_EL2(value) HW_CPU_SET_SYSREG(cnthctl_el2, value) + + #define HW_CPU_GET_ACTLR_EL2(value) HW_CPU_GET_SYSREG(actlr_el2, value) + #define HW_CPU_SET_ACTLR_EL2(value) HW_CPU_SET_SYSREG(actlr_el2, value) + + #define HW_CPU_GET_ACTLR_EL3(value) HW_CPU_GET_SYSREG(actlr_el3, value) + #define HW_CPU_SET_ACTLR_EL3(value) HW_CPU_SET_SYSREG(actlr_el3, value) + + #define HW_CPU_GET_HCR_EL2(value) HW_CPU_GET_SYSREG(hcr_el2, value) + #define HW_CPU_SET_HCR_EL2(value) HW_CPU_SET_SYSREG(hcr_el2, value) + + #define HW_CPU_GET_DACR32_EL2(value) HW_CPU_GET_SYSREG(dacr32_el2, value) + #define HW_CPU_SET_DACR32_EL2(value) HW_CPU_SET_SYSREG(dacr32_el2, value) + + #define HW_CPU_GET_SCTLR_EL2(value) HW_CPU_GET_SYSREG(sctlr_el2, value) + #define HW_CPU_SET_SCTLR_EL2(value) HW_CPU_SET_SYSREG(sctlr_el2, value) + + #define HW_CPU_GET_SCTLR_EL1(value) HW_CPU_GET_SYSREG(sctlr_el1, value) + #define HW_CPU_SET_SCTLR_EL1(value) HW_CPU_SET_SYSREG(sctlr_el1, value) + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/system-control-register-el3 */ struct SctlrEl3 { - using M = util::BitPack32::Field< 0, 1>; - using A = util::BitPack32::Field< 1, 1>; - using C = util::BitPack32::Field< 2, 1>; - using Sa = util::BitPack32::Field< 3, 1>; - using I = util::BitPack32::Field<12, 1>; - using Wxn = util::BitPack32::Field<19, 1>; - using Ee = util::BitPack32::Field<25, 1>; + using M = util::BitPack64::Field< 0, 1>; + using A = util::BitPack64::Field< 1, 1>; + using C = util::BitPack64::Field< 2, 1>; + using Sa = util::BitPack64::Field< 3, 1>; + using I = util::BitPack64::Field<12, 1>; + using Wxn = util::BitPack64::Field<19, 1>; + using Ee = util::BitPack64::Field<25, 1>; - static constexpr u32 Res1 = 0x30C50830; + static constexpr u64 Res1 = 0x30C50830; + }; + + /* https://static.docs.arm.com/ddi0487/fb/DDI0487F_b_armv8_arm.pdf */ + struct SctlrEl2 { + using M = util::BitPack64::Field< 0, 1>; + using A = util::BitPack64::Field< 1, 1>; + using C = util::BitPack64::Field< 2, 1>; + using Sa = util::BitPack64::Field< 3, 1>; + using I = util::BitPack64::Field<12, 1>; + using Wxn = util::BitPack64::Field<19, 1>; + using Ee = util::BitPack64::Field<25, 1>; + + static constexpr u64 Res1 = 0x30C50830; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/system-control-register-el1 */ + struct SctlrEl1 { + using M = util::BitPack64::Field< 0, 1>; + using A = util::BitPack64::Field< 1, 1>; + using C = util::BitPack64::Field< 2, 1>; + using Sa = util::BitPack64::Field< 3, 1>; + using Sa0 = util::BitPack64::Field< 4, 1>; + using Cp15BEn = util::BitPack64::Field< 5, 1>; + using Thee = util::BitPack64::Field< 6, 1>; + using Itd = util::BitPack64::Field< 7, 1>; + using Sed = util::BitPack64::Field< 8, 1>; + using Uma = util::BitPack64::Field< 9, 1>; + using I = util::BitPack64::Field<12, 1>; + using Dze = util::BitPack64::Field<14, 1>; + using Uct = util::BitPack64::Field<15, 1>; + using Ntwi = util::BitPack64::Field<16, 1>; + using Ntwe = util::BitPack64::Field<18, 1>; + using Wxn = util::BitPack64::Field<19, 1>; + using E0e = util::BitPack64::Field<24, 1>; + using Ee = util::BitPack64::Field<25, 1>; + using Uci = util::BitPack64::Field<26, 1>; + + static constexpr u64 Res1 = 0x30D00800; }; /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABGIHHJ.html */ @@ -242,4 +304,27 @@ namespace ams::hw::arch::arm64 { using Snid = util::BitPack32::Field<6, 2>; }; -} \ No newline at end of file + /* https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/cnthctl_el2 */ + struct CnthctlEl2 { + using El1PctEn = util::BitPack32::Field<0, 1>; + using El1PcEn = util::BitPack32::Field<1, 1>; + using EvntEn = util::BitPack32::Field<2, 1>; + using EvntDir = util::BitPack32::Field<3, 1>; + using EvntI = util::BitPack32::Field<4, 4>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/auxiliary-control-register-el3 */ + struct ActlrCortexA57 { + using Cpuactlr = util::BitPack32::Field<0, 1>; + using Cpuectlr = util::BitPack32::Field<1, 1>; + using L2ctlr = util::BitPack32::Field<4, 1>; + using L2ectlr = util::BitPack32::Field<5, 1>; + using L2actlr = util::BitPack32::Field<6, 1>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/hypervisor-configuration-register-el2 */ + struct HcrEl2 { + using Rw = util::BitPack64::Field<31, 1>; + }; + +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index d7d3361e1..eca5115ff 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -20,6 +20,21 @@ #define FLOW_CTLR_FLOW_DBG_QUAL (0x050) #define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098) +#define FLOW_CTLR_CPU0_CSR (0x008) +#define FLOW_CTLR_CPU1_CSR (0x018) +#define FLOW_CTLR_CPU2_CSR (0x020) +#define FLOW_CTLR_CPU3_CSR (0x028) + +#define FLOW_CTLR_HALT_CPU0_EVENTS (0x000) +#define FLOW_CTLR_HALT_CPU1_EVENTS (0x014) +#define FLOW_CTLR_HALT_CPU2_EVENTS (0x01C) +#define FLOW_CTLR_HALT_CPU3_EVENTS (0x024) + +#define FLOW_CTLR_CC4_CORE0_CTRL (0x06C) +#define FLOW_CTLR_CC4_CORE1_CTRL (0x070) +#define FLOW_CTLR_CC4_CORE2_CTRL (0x074) +#define FLOW_CTLR_CC4_CORE3_CTRL (0x078) + #define FLOW_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FLOW_CTLR, NAME) #define FLOW_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FLOW_CTLR, NAME, VALUE) #define FLOW_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FLOW_CTLR, NAME, ENUM) diff --git a/libraries/libexosphere/source/flow/flow_api.cpp b/libraries/libexosphere/source/flow/flow_api.cpp new file mode 100644 index 000000000..613c78c07 --- /dev/null +++ b/libraries/libexosphere/source/flow/flow_api.cpp @@ -0,0 +1,51 @@ +/* + * 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> + +namespace ams::flow { + + namespace { + + struct FlowControllerRegisterOffset { + u16 cpu_csr; + u16 halt_cpu_events; + u16 cc4_core_ctrl; + }; + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + + constexpr const FlowControllerRegisterOffset FlowControllerRegisterOffsets[] = { + { FLOW_CTLR_CPU0_CSR, FLOW_CTLR_HALT_CPU0_EVENTS, FLOW_CTLR_CC4_CORE0_CTRL, }, + { FLOW_CTLR_CPU1_CSR, FLOW_CTLR_HALT_CPU1_EVENTS, FLOW_CTLR_CC4_CORE1_CTRL, }, + { FLOW_CTLR_CPU2_CSR, FLOW_CTLR_HALT_CPU2_EVENTS, FLOW_CTLR_CC4_CORE2_CTRL, }, + { FLOW_CTLR_CPU3_CSR, FLOW_CTLR_HALT_CPU3_EVENTS, FLOW_CTLR_CC4_CORE3_CTRL, }, + }; + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void ResetCpuRegisters(int core) { + AMS_ASSUME(core >= 0); + + const auto &offsets = FlowControllerRegisterOffsets[core]; + reg::Write(g_register_address + offsets.cpu_csr, 0); + reg::Write(g_register_address + offsets.halt_cpu_events, 0); + } + +} \ No newline at end of file From e11fad6598c8676432d2d6b5dc4e33510cbd39ab Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 00:32:09 -0700 Subject: [PATCH 043/118] exo2: implement through boot config load/validate --- .../program/source/boot/secmon_boot.hpp | 6 + .../source/boot/secmon_boot_config.cpp | 16 +- .../source/boot/secmon_boot_functions.cpp | 81 +++++++++ .../source/boot/secmon_boot_functions.hpp | 7 + .../program/source/boot/secmon_boot_rsa.cpp | 159 ++++++++++++++++++ .../program/source/boot/secmon_main.cpp | 22 ++- libraries/libexosphere/include/exosphere.hpp | 1 + .../libexosphere/include/exosphere/br.hpp | 19 +++ .../include/exosphere/br/br_types.hpp | 30 ++++ .../exosphere/br/impl/br_erista_types.hpp | 21 +++ .../exosphere/br/impl/br_mariko_types.hpp | 21 +++ .../libexosphere/include/exosphere/fuse.hpp | 2 + .../libexosphere/include/exosphere/pkg1.hpp | 1 + .../include/exosphere/pkg1/pkg1_api.hpp | 25 +++ .../exosphere/pkg1/pkg1_boot_config.hpp | 6 +- .../libexosphere/include/exosphere/se.hpp | 1 + .../include/exosphere/se/se_hash.hpp | 30 ++++ .../include/exosphere/se/se_rsa.hpp | 2 + ...ecmon_configuration_context.arch.arm64.hpp | 4 + .../exosphere/secmon/secmon_memory_layout.hpp | 8 +- .../include/exosphere/tegra/tegra_sysctr0.hpp | 19 ++- .../libexosphere/source/fuse/fuse_api.cpp | 26 ++- .../libexosphere/source/pkg1/pkg1_api.cpp | 40 +++++ libraries/libexosphere/source/se/se_hash.cpp | 72 ++++++++ .../libexosphere/source/se/se_registers.hpp | 42 ++--- libraries/libexosphere/source/se/se_rsa.cpp | 76 +++++++-- 26 files changed, 688 insertions(+), 49 deletions(-) create mode 100644 exosphere2/program/source/boot/secmon_boot_rsa.cpp create mode 100644 libraries/libexosphere/include/exosphere/br.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/br_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_hash.hpp create mode 100644 libraries/libexosphere/source/pkg1/pkg1_api.cpp create mode 100644 libraries/libexosphere/source/se/se_hash.cpp diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp index dca6d0091..d82c67cac 100644 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -22,4 +22,10 @@ namespace ams::secmon::boot { void InitializeColdBoot(); + bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size); + bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size); + + bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size); + bool VerifyBootConfigEcid(const pkg1::BootConfig &bc); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp index ad0649c3e..c4448624a 100644 --- a/exosphere2/program/source/boot/secmon_boot_config.cpp +++ b/exosphere2/program/source/boot/secmon_boot_config.cpp @@ -14,9 +14,21 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "secmon_boot.hpp" namespace ams::secmon::boot { - /* TODO */ + bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size) { + return VerifySignature(std::addressof(bc.signature), sizeof(bc.signature), mod, mod_size, std::addressof(bc.signed_data), sizeof(bc.signed_data)); + } -} \ No newline at end of file + bool VerifyBootConfigEcid(const pkg1::BootConfig &bc) { + /* Get the ecid. */ + br::BootEcid ecid; + fuse::GetEcid(std::addressof(ecid)); + + /* Verify it matches. */ + return crypto::IsSameBytes(std::addressof(ecid), bc.signed_data.ecid, sizeof(ecid)); + } + +} diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 3095f2f77..a9488e44e 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -14,13 +14,94 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "secmon_boot.hpp" #include "secmon_boot_functions.hpp" namespace ams::secmon::boot { + namespace { + + constexpr inline uintptr_t SYSCTR0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); + + constinit const u8 BootConfigRsaPublicModulus[se::RsaSize] = { + 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, + 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, + 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, + 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, + 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, + 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, + 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, + 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, + 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, + 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, + 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, + 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, + 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, + 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, + 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, + 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D + }; + + } + void ClearIram() { /* Clear the boot code image from where it was loaded in IRAM. */ util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize()); } + void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state) { + /* Check NX Bootloader's state once per microsecond until it's advanced enough. */ + while (params.bootloader_state < state) { + util::WaitMicroSeconds(1); + } + } + + void LoadBootConfig(const void *src) { + pkg1::BootConfig * const dst = secmon::impl::GetBootConfigStorage(); + + if (pkg1::IsProduction()) { + std::memset(dst, 0, sizeof(*dst)); + } else { + hw::FlushDataCache(src, sizeof(*dst)); + hw::DataSynchronizationBarrierInnerShareable(); + std::memcpy(dst, src, sizeof(*dst)); + } + } + + void VerifyOrClearBootConfig() { + /* On production hardware, the boot config is already cleared. */ + if (pkg1::IsProduction()) { + return; + } + + pkg1::BootConfig * const bc = secmon::impl::GetBootConfigStorage(); + + /* Determine if the bc is valid for the device. */ + bool valid_for_device = false; + { + const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, BootConfigRsaPublicModulus, util::size(BootConfigRsaPublicModulus)); + if (valid_signature) { + valid_for_device = secmon::boot::VerifyBootConfigEcid(*bc); + } + } + + /* If the boot config is not valid for the device, clear its signed data. */ + if (!valid_for_device) { + util::ClearMemory(std::addressof(bc->signed_data), sizeof(bc->signed_data)); + } + } + + void EnableTsc(u64 initial_tsc_value) { + /* Write the initial value to the CNTCV registers. */ + const u32 lo = static_cast<u32>(initial_tsc_value >> 0); + const u32 hi = static_cast<u32>(initial_tsc_value >> 32); + + reg::Write(SYSCTR0 + SYSCTR0_CNTCV0, lo); + reg::Write(SYSCTR0 + SYSCTR0_CNTCV1, hi); + + /* Configure the system counter control register. */ + reg::Write(SYSCTR0 + SYSCTR0_CNTCR, SYSCTR0_REG_BITS_ENUM(CNTCR_HDBG, ENABLE), + SYSCTR0_REG_BITS_ENUM(CNTCR_EN, ENABLE)); + } + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp index 5ec201add..83ac091db 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -20,4 +20,11 @@ namespace ams::secmon::boot { void ClearIram(); + void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state); + + void LoadBootConfig(const void *src); + void VerifyOrClearBootConfig(); + + void EnableTsc(u64 initial_tsc_value); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_rsa.cpp b/exosphere2/program/source/boot/secmon_boot_rsa.cpp new file mode 100644 index 000000000..344196c05 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_rsa.cpp @@ -0,0 +1,159 @@ +/* + * 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_boot.hpp" + +namespace ams::secmon::boot { + + namespace { + + constinit const u8 RsaPublicKeyExponent[] = { + 0x00, 0x01, 0x00, 0x01, + }; + + constexpr inline u8 TailMagic = 0xBC; + + bool VerifyRsaPssSha256(const u8 *sig, const void *msg, size_t msg_size) { + /* Define constants. */ + constexpr int EmBits = 2047; + constexpr int EmLen = util::DivideUp(EmBits, BITSIZEOF(u8)); + constexpr int SaltLen = 0x20; + constexpr int HashLen = se::Sha256HashSize; + + /* Define a work buffer. */ + u8 work[EmLen]; + ON_SCOPE_EXIT { util::ClearMemory(work, sizeof(work)); }; + + /* Calculate the message hash, first flushing cache to ensure SE sees correct data. */ + se::Sha256Hash msg_hash; + hw::FlushDataCache(msg, msg_size); + hw::DataSynchronizationBarrierInnerShareable(); + se::CalculateSha256(std::addressof(msg_hash), msg, msg_size); + + /* Verify the tail magic. */ + bool is_valid = sig[EmLen - 1] == TailMagic; + + /* Determine extents of masked db and h. */ + const u8 *masked_db = std::addressof(sig[0]); + const u8 *h = std::addressof(sig[EmLen - HashLen - 1]); + + /* Verify the extra bits are zero. */ + is_valid &= (masked_db[0] >> (BITSIZEOF(u8) - (BITSIZEOF(u8) * EmLen - EmBits))) == 0; + + /* Calculate the db mask. */ + { + constexpr int MaskLen = EmLen - HashLen - 1; + constexpr int HashIters = util::DivideUp(MaskLen, HashLen); + + u8 mgf1_buf[sizeof(u32) + HashLen]; + + std::memcpy(std::addressof(mgf1_buf[0]), h, HashLen); + std::memset(std::addressof(mgf1_buf[HashLen]), 0, sizeof(u32)); + + for (int i = 0; i < HashIters; ++i) { + /* Set the counter for this iteration. */ + mgf1_buf[sizeof(mgf1_buf) - 1] = i; + + /* Calculate the sha256 to the appropriate place in the work buffer. */ + auto *mgf1_dst = reinterpret_cast<se::Sha256Hash *>(std::addressof(work[HashLen * i])); + + hw::FlushDataCache(mgf1_buf, sizeof(mgf1_buf)); + hw::DataSynchronizationBarrierInnerShareable(); + se::CalculateSha256(mgf1_dst, mgf1_buf, sizeof(mgf1_buf)); + } + } + + /* Decrypt masked db using the mask we just generated. */ + for (int i = 0; i < EmLen - HashLen - 1; ++i) { + work[i] ^= masked_db[i]; + } + + /* Mask out the top bits. */ + u8 *db = work; + db[0] &= 0xFF >> (BITSIZEOF(u8) * EmLen - EmBits); + + /* Verify that DB is of the form 0000...0001 */ + constexpr int DbLen = EmLen - HashLen - 1; + int salt_ofs = 0; + { + int looking_for_one = 1; + int invalid_db_padding = 0; + int is_zero; + int is_one; + for (size_t i = 0; i < DbLen; /* ... */) { + is_zero = (db[i] == 0); + is_one = (db[i] == 1); + salt_ofs += (looking_for_one & is_one) * (static_cast<s32>(++i)); + looking_for_one &= ~is_one; + invalid_db_padding |= (looking_for_one & ~is_zero); + } + + is_valid &= (invalid_db_padding == 0); + } + + /* Verify salt. */ + is_valid &= (DbLen - salt_ofs) == SaltLen; + + /* Setup the message to verify. */ + const u8 *salt = std::addressof(db[DbLen - SaltLen]); + u8 verif_msg[8 + HashLen + SaltLen]; + ON_SCOPE_EXIT { util::ClearMemory(verif_msg, sizeof(verif_msg)); }; + + util::ClearMemory(std::addressof(verif_msg[0]), 8); + std::memcpy(std::addressof(verif_msg[8]), std::addressof(msg_hash), HashLen); + std::memcpy(std::addressof(verif_msg[8 + HashLen]), salt, SaltLen); + + /* Verify the final hash. */ + return VerifyHash(h, reinterpret_cast<uintptr_t>(std::addressof(verif_msg[0])), sizeof(verif_msg)); + } + + bool VerifyRsaPssSha256(int slot, void *sig, size_t sig_size, const void *msg, size_t msg_size) { + /* Exponentiate the signature, using the signature as the destination buffer. */ + se::ModularExponentiate(sig, sig_size, slot, sig, sig_size); + + /* Verify the pss padding. */ + return VerifyRsaPssSha256(static_cast<const u8 *>(sig), msg, msg_size); + } + + } + + bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size) { + /* Load the public key into a temporary keyslot. */ + const int slot = pkg1::RsaKeySlot_Temporary; + se::SetRsaKey(slot, mod, mod_size, RsaPublicKeyExponent, util::size(RsaPublicKeyExponent)); + + return VerifyRsaPssSha256(slot, sig, sig_size, msg, msg_size); + } + + bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size) { + /* Zero-sized messages are always valid. */ + if (msg_size == 0) { + return true; + } + + /* Ensure that the SE sees correct data for the message. */ + hw::FlushDataCache(reinterpret_cast<void *>(msg), msg_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + se::Sha256Hash calc_hash; + se::CalculateSha256(std::addressof(calc_hash), reinterpret_cast<void *>(msg), msg_size); + + /* Verify the result. */ + return crypto::IsSameBytes(std::addressof(calc_hash), hash, sizeof(calc_hash)); + } + +} diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 9818f7ef7..93b9f9c93 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -26,7 +26,7 @@ namespace ams::secmon { /* Set library register addresses. */ /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); - /* flowctrl::SetRegisterAddress(); */ + flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); @@ -69,6 +69,26 @@ namespace ams::secmon { /* Initialize the random cache. */ secmon::smc::FillRandomCache(); } + + /* Wait for NX Bootloader to finish loading the BootConfig. */ + secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedBootConfig); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Load the bootconfig. */ + secmon::boot::LoadBootConfig(MemoryRegionPhysicalIramBootConfig.GetPointer()); + + /* Verify or clear the boot config. */ + secmon::boot::VerifyOrClearBootConfig(); + + /* Get the boot config. */ + const auto &bc = secmon::GetBootConfig(); + + /* Set the tsc value by the boot config. */ + { + constexpr u64 TscMask = (static_cast<u64>(1) << 55) - 1; + + secmon::boot::EnableTsc(bc.data.GetInitialTscValue() & TscMask); + } } } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index c92a382d9..b320a52e5 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -21,6 +21,7 @@ #include <exosphere/hw.hpp> #include <exosphere/util.hpp> #include <exosphere/mmu.hpp> +#include <exosphere/br.hpp> #include <exosphere/gic.hpp> #include <exosphere/wdt.hpp> #include <exosphere/pkg1.hpp> diff --git a/libraries/libexosphere/include/exosphere/br.hpp b/libraries/libexosphere/include/exosphere/br.hpp new file mode 100644 index 000000000..089713a08 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br.hpp @@ -0,0 +1,19 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#include <exosphere/br/br_types.hpp> diff --git a/libraries/libexosphere/include/exosphere/br/br_types.hpp b/libraries/libexosphere/include/exosphere/br/br_types.hpp new file mode 100644 index 000000000..382010755 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/br_types.hpp @@ -0,0 +1,30 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#include <exosphere/br/impl/br_erista_types.hpp> +#include <exosphere/br/impl/br_mariko_types.hpp> + +namespace ams::br { + + struct BootEcid { + u32 ecid[4]; + }; + static_assert(util::is_pod<BootEcid>::value); + static_assert(sizeof(BootEcid) == 0x10); + +} diff --git a/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp b/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp new file mode 100644 index 000000000..ee26f5b9e --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp @@ -0,0 +1,21 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::br::erista { + +} diff --git a/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp b/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp new file mode 100644 index 000000000..8210479c2 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp @@ -0,0 +1,21 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::br::mariko { + +} diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index 3df44b8a3..55efdb334 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -15,6 +15,7 @@ */ #pragma once #include <vapours.hpp> +#include <exosphere/br.hpp> #include <exosphere/pmic.hpp> namespace ams::fuse { @@ -45,5 +46,6 @@ namespace ams::fuse { HardwareType GetHardwareType(); HardwareState GetHardwareState(); pmic::Regulator GetRegulator(); + void GetEcid(br::BootEcid *out); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg1.hpp b/libraries/libexosphere/include/exosphere/pkg1.hpp index d7dbad6af..90a61cb17 100644 --- a/libraries/libexosphere/include/exosphere/pkg1.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1.hpp @@ -20,3 +20,4 @@ #include <exosphere/pkg1/pkg1_error_types.hpp> #include <exosphere/pkg1/pkg1_key_generation.hpp> #include <exosphere/pkg1/pkg1_se_key_slots.hpp> +#include <exosphere/pkg1/pkg1_api.hpp> diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp new file mode 100644 index 000000000..632a122ac --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg1 { + + bool IsProduction(); + bool IsProductionForVersionCheck(); + bool IsProductionForPublicKey(); + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp index da73f0112..8cef0c823 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -90,9 +90,13 @@ namespace ams::pkg1 { return static_cast<MemoryMode>(this->flags0[3]); } - bool IsTscInitialValueValid() const { + constexpr bool IsInitialTscValueValid() const { return (this->flags0[4] & (1 << 0)) != 0; } + + constexpr u64 GetInitialTscValue() const { + return this->IsInitialTscValueValid() ? this->initial_tsc_value : 0; + } }; static_assert(util::is_pod<BootConfigData>::value); static_assert(sizeof(BootConfigData) == 0x200); diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp index ad9abd632..c46693815 100644 --- a/libraries/libexosphere/include/exosphere/se.hpp +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -19,6 +19,7 @@ #include <exosphere/se/se_common.hpp> #include <exosphere/se/se_management.hpp> #include <exosphere/se/se_aes.hpp> +#include <exosphere/se/se_hash.hpp> #include <exosphere/se/se_rsa.hpp> #include <exosphere/se/se_rng.hpp> #include <exosphere/se/se_suspend.hpp> diff --git a/libraries/libexosphere/include/exosphere/se/se_hash.hpp b/libraries/libexosphere/include/exosphere/se/se_hash.hpp new file mode 100644 index 000000000..934ced609 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_hash.hpp @@ -0,0 +1,30 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::se { + + constexpr inline int Sha256HashSize = crypto::Sha256Generator::HashSize; + + union Sha256Hash { + u8 bytes[Sha256HashSize / sizeof( u8)]; + u32 words[Sha256HashSize / sizeof(u32)]; + }; + + void CalculateSha256(Sha256Hash *dst, const void *src, size_t src_size); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp index e16b99c66..e632f67a9 100644 --- a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -26,4 +26,6 @@ namespace ams::se { void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); + void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp index 7f094a14b..bf8515650 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -70,6 +70,10 @@ namespace ams::secmon { GetConfigurationContext().secmon_cfg.key_generation = generation; } + ALWAYS_INLINE pkg1::BootConfig *GetBootConfigStorage() { + return std::addressof(GetConfigurationContext().boot_config); + } + } ALWAYS_INLINE const ConfigurationContext &GetConfigurationContext() { diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index c40368a3c..1e25108c5 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -271,7 +271,9 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramL2L3PageTable)); static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramL2L3PageTable)); - constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(0x7C010800, 0xD800); - constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(0x40032000, 0xC000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(UINT64_C(0x7C010800), 0xD800); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(UINT64_C(0x40032000), 0xC000); -} \ No newline at end of file + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootConfig = MemoryRegion(UINT64_C(0x4003F800), 0x400); + +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp index a191641f9..d7e7fd497 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp @@ -16,6 +16,9 @@ #pragma once #include <vapours.hpp> +#define SYSCTR0_CNTCR (0x00C) +#define SYSCTR0_CNTCV0 (0x008) +#define SYSCTR0_CNTCV1 (0x00C) #define SYSCTR0_CNTFID0 (0x020) #define SYSCTR0_CNTFID1 (0x024) @@ -33,4 +36,18 @@ #define SYSCTR0_COUNTERID10 (0xFF8) #define SYSCTR0_COUNTERID11 (0xFFC) -#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n \ No newline at end of file +#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n + +#define SYSCTR0_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SYSCTR0, NAME) +#define SYSCTR0_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SYSCTR0, NAME, VALUE) +#define SYSCTR0_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SYSCTR0, NAME, ENUM) +#define SYSCTR0_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SYSCTR0, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_SYSCTR0_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SYSCTR0, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_SYSCTR0_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_SYSCTR0_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_SYSCTR0_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_SYSCTR0_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_EN, 0, DISABLE, ENABLE); +DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_HDBG, 1, DISABLE, ENABLE); \ No newline at end of file diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index 73f48494d..32d491393 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -118,4 +118,28 @@ namespace ams::fuse { return pmic::Regulator_Erista_Max77621; } -} \ No newline at end of file + void GetEcid(br::BootEcid *out) { + /* Get the registers. */ + const volatile auto &chip = GetChipRegisters(); + + /* Read the ecid components. */ + const u32 vendor = reg::Read(chip.FUSE_OPT_VENDOR_CODE); + const u32 fab = reg::Read(chip.FUSE_OPT_FAB_CODE); + const u32 lot0 = reg::Read(chip.FUSE_OPT_LOT_CODE_0); + const u32 lot1 = reg::Read(chip.FUSE_OPT_LOT_CODE_1); + const u32 wafer = reg::Read(chip.FUSE_OPT_WAFER_ID); + const u32 x_coord = reg::Read(chip.FUSE_OPT_X_COORDINATE); + const u32 y_coord = reg::Read(chip.FUSE_OPT_Y_COORDINATE); + const u32 reserved = reg::Read(chip.FUSE_OPT_OPS_RESERVED); + + /* Clear the output. */ + util::ClearMemory(out, sizeof(*out)); + + /* Copy the component bits. */ + out->ecid[0] = static_cast<u32>((lot1 << 30) | (wafer << 24) | (x_coord << 15) | (y_coord << 6) | (reserved)); + out->ecid[1] = static_cast<u32>((lot0 << 26) | (lot1 >> 2)); + out->ecid[2] = static_cast<u32>((fab << 26) | (lot0 >> 6)); + out->ecid[3] = static_cast<u32>(vendor); + } + +} diff --git a/libraries/libexosphere/source/pkg1/pkg1_api.cpp b/libraries/libexosphere/source/pkg1/pkg1_api.cpp new file mode 100644 index 000000000..035b4d087 --- /dev/null +++ b/libraries/libexosphere/source/pkg1/pkg1_api.cpp @@ -0,0 +1,40 @@ +/* + * 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> + +namespace ams::pkg1 { + + namespace { + + bool IsProductionImpl() { + return fuse::GetHardwareState() != fuse::HardwareState_Development; + } + + } + + bool IsProduction() { + return IsProductionImpl(); + } + + bool IsProductionForVersionCheck() { + return IsProductionImpl(); + } + + bool IsProductionForPublicKey() { + return IsProductionImpl(); + } + +} diff --git a/libraries/libexosphere/source/se/se_hash.cpp b/libraries/libexosphere/source/se/se_hash.cpp new file mode 100644 index 000000000..2ef38deb2 --- /dev/null +++ b/libraries/libexosphere/source/se/se_hash.cpp @@ -0,0 +1,72 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + void SetMessageSize(volatile SecurityEngineRegisters *SE, size_t src_size) { + /* Set the message size. */ + reg::Write(SE->SE_SHA_MSG_LENGTH[0], src_size * BITSIZEOF(u8)); + reg::Write(SE->SE_SHA_MSG_LENGTH[1], 0); + reg::Write(SE->SE_SHA_MSG_LENGTH[2], 0); + reg::Write(SE->SE_SHA_MSG_LENGTH[3], 0); + + /* Set the message remaining size. */ + reg::Write(SE->SE_SHA_MSG_LEFT[0], src_size * BITSIZEOF(u8)); + reg::Write(SE->SE_SHA_MSG_LEFT[1], 0); + reg::Write(SE->SE_SHA_MSG_LEFT[2], 0); + reg::Write(SE->SE_SHA_MSG_LEFT[3], 0); + } + + void GetHashResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) { + /* Copy out the words. */ + const int num_words = dst_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + const u32 word = reg::Read(SE->SE_HASH_RESULT[i]); + util::StoreBigEndian(static_cast<u32 *>(dst) + i, word); + } + } + + } + + void CalculateSha256(Sha256Hash *dst, const void *src, size_t src_size) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the engine to perform SHA256 "encryption". */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, SHA256), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, SHA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, HASH_REG)); + + /* Begin a hardware hash operation. */ + reg::Write(SE->SE_SHA_CONFIG, SE_REG_BITS_VALUE(SHA_CONFIG_HW_INIT_HASH, 1)); + + /* Set the message size. */ + SetMessageSize(SE, src_size); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, src, src_size); + + /* Get the result. */ + GetHashResult(SE, dst, sizeof(*dst)); + } + +} diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp index d76e0571c..2a5e70352 100644 --- a/libraries/libexosphere/source/se/se_registers.hpp +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -133,32 +133,12 @@ namespace ams::se { DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_ERR_STAT, 16); /* SE_CONFIG */ - DEFINE_SE_REG(CONFIG_DST, 2, 3); - DEFINE_SE_REG(CONFIG_DEC_ALG, 8, 4); - DEFINE_SE_REG(CONFIG_ENC_ALG, 12, 4); DEFINE_SE_REG(CONFIG_DEC_MODE, 16, 8); DEFINE_SE_REG(CONFIG_ENC_MODE, 24, 8); - enum SE_CONFIG_DST { - SE_CONFIG_DST_MEMORY = 0, - SE_CONFIG_DST_HASH_REG = 1, - SE_CONFIG_DST_KEYTABLE = 2, - SE_CONFIG_DST_SRK = 3, - SE_CONFIG_DST_RSA_REG = 4, - }; - - enum SE_CONFIG_DEC_ALG { - SE_CONFIG_DEC_ALG_NOP = 0, - SE_CONFIG_DEC_ALG_AES_DEC = 1, - }; - - enum SE_CONFIG_ENC_ALG { - SE_CONFIG_ENC_ALG_NOP = 0, - SE_CONFIG_ENC_ALG_AES_ENC = 1, - SE_CONFIG_ENC_ALG_RNG = 2, - SE_CONFIG_ENC_ALG_SHA = 3, - SE_CONFIG_ENC_ALG_RSA = 4, - }; + DEFINE_SE_REG_THREE_BIT_ENUM(CONFIG_DST, 2, MEMORY, HASH_REG, KEYTABLE, SRK, RSA_REG, RESERVED5, RESERVED6, RESERVED7); + DEFINE_SE_REG_FOUR_BIT_ENUM(CONFIG_DEC_ALG, 8, NOP, AES_DEC, RESERVED2, RESERVED3, RESERVED4, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_SE_REG_FOUR_BIT_ENUM(CONFIG_ENC_ALG, 12, NOP, AES_ENC, RNG, SHA, RSA, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); enum SE_CONFIG_DEC_MODE { SE_CONFIG_DEC_MODE_AESMODE_KEY128 = 0, @@ -171,13 +151,16 @@ namespace ams::se { SE_CONFIG_ENC_MODE_AESMODE_KEY192 = 1, SE_CONFIG_ENC_MODE_AESMODE_KEY256 = 2, - SE_CONFIG_ENC_MODE_AESMODE_SHA1 = 1, - SE_CONFIG_ENC_MODE_AESMODE_SHA224 = 4, - SE_CONFIG_ENC_MODE_AESMODE_SHA256 = 5, - SE_CONFIG_ENC_MODE_AESMODE_SHA384 = 6, - SE_CONFIG_ENC_MODE_AESMODE_SHA512 = 7, + SE_CONFIG_ENC_MODE_SHA1 = 1, + SE_CONFIG_ENC_MODE_SHA224 = 4, + SE_CONFIG_ENC_MODE_SHA256 = 5, + SE_CONFIG_ENC_MODE_SHA384 = 6, + SE_CONFIG_ENC_MODE_SHA512 = 7, }; + /* SE_SHA_CONFIG */ + DEFINE_SE_REG(SHA_CONFIG_HW_INIT_HASH, 0, 1); + /* SE_CRYPTO_KEYTABLE_ADDR */ DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, 0, 4); @@ -208,6 +191,9 @@ namespace ams::se { DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, 4, 4); + /* SE_RSA_CONFIG */ + DEFINE_SE_REG(RSA_CONFIG_KEY_SLOT, 24, 1); + /* SE_RSA_KEYTABLE_ADDR */ DEFINE_SE_REG(RSA_KEYTABLE_ADDR_WORD_ADDR, 0, 6); DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_EXPMOD_SEL, 6, EXPONENT, MODULUS); diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp index d39a040dc..99ec5e90a 100644 --- a/libraries/libexosphere/source/se/se_rsa.cpp +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -27,10 +27,7 @@ namespace ams::se { constinit RsaKeyInfo g_rsa_key_infos[RsaKeySlotCount] = {}; - void ClearRsaKeySlot(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { - /* Get the engine. */ - auto *SE = GetRegisters(); - + void ClearRsaKeySlot(volatile SecurityEngineRegisters *SE, int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { constexpr int NumWords = se::RsaSize / sizeof(u32); for (int i = 0; i < NumWords; ++i) { /* Select the keyslot word. */ @@ -44,10 +41,7 @@ namespace ams::se { } } - void SetRsaKey(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { - /* Get the engine. */ - auto *SE = GetRegisters(); - + void SetRsaKey(volatile SecurityEngineRegisters *SE, int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { const int num_words = key_size / sizeof(u32); for (int i = 0; i < num_words; ++i) { /* Select the keyslot word. */ @@ -64,6 +58,15 @@ namespace ams::se { } } + void GetRsaResult(volatile SecurityEngineRegisters *SE, void *dst, size_t size) { + /* Copy out the words. */ + const int num_words = size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + const u32 word = reg::Read(SE->SE_RSA_OUTPUT[i]); + util::StoreBigEndian(static_cast<u32 *>(dst) + num_words - 1 - i, word); + } + } + } void ClearRsaKeySlot(int slot) { @@ -73,11 +76,14 @@ namespace ams::se { /* Clear the info. */ g_rsa_key_infos[slot] = {}; + /* Get the engine. */ + auto *SE = GetRegisters(); + /* Clear the modulus. */ - ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); + ClearRsaKeySlot(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); /* Clear the exponent. */ - ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); + ClearRsaKeySlot(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); } void LockRsaKeySlot(int slot, u32 flags) { @@ -117,9 +123,55 @@ namespace ams::se { info.modulus_size_val = (mod_size / 64) - 1; info.exponent_size_val = (exp_size / 4); + /* Get the engine. */ + auto *SE = GetRegisters(); + /* Set the modulus and exponent. */ - SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); - SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + SetRsaKey(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); + SetRsaKey(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + } + + void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + /* Validate the slot and sizes. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(src_size <= RsaSize); + AMS_ABORT_UNLESS(dst_size <= RsaSize); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Create a work buffer. */ + u8 work[RsaSize]; + util::ClearMemory(work, sizeof(work)); + + /* Copy the input into the work buffer (reversing endianness). */ + const u8 *src_u8 = static_cast<const u8 *>(src); + for (size_t i = 0; i < src_size; ++i) { + work[src_size - 1 - i] = src_u8[i]; + } + + /* Flush the work buffer to ensure the SE sees correct results. */ + hw::FlushDataCache(work, sizeof(work)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the engine to perform RSA encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG)); + + /* Configure the engine to use the keyslot and correct modulus/exp sizes. */ + const auto &info = g_rsa_key_infos[slot]; + reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot)); + reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val); + reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, work, src_size); + + /* Copy out the result. */ + GetRsaResult(SE, dst, dst_size); } } From 9ddcbe9dc30552b0a1dfe47b925fccbe130f5cf0 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 01:18:10 -0700 Subject: [PATCH 044/118] exo2: implement main through sync-for-pk21-load --- .../program/source/boot/secmon_boot.hpp | 1 + .../program/source/boot/secmon_boot_cache.cpp | 3 +- .../program/source/boot/secmon_boot_cache.hpp | 23 +++++++++++++ .../source/boot/secmon_boot_functions.cpp | 27 +++++++++++++++ .../source/boot/secmon_boot_functions.hpp | 2 ++ .../program/source/boot/secmon_boot_setup.cpp | 34 ++++++++++++++++++- .../program/source/boot/secmon_main.cpp | 30 ++++++++++++++++ .../source/boot/secmon_make_page_table.cpp | 2 +- exosphere2/program/source/secmon_cache.inc | 2 +- exosphere2/program/source/secmon_setup.cpp | 10 ++++++ exosphere2/program/source/secmon_setup.hpp | 2 ++ .../exosphere/secmon/secmon_memory_layout.hpp | 6 +++- .../include/exosphere/tegra/tegra_sysctr0.hpp | 2 +- 13 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 exosphere2/program/source/boot/secmon_boot_cache.hpp diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp index d82c67cac..1214b8850 100644 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -19,6 +19,7 @@ namespace ams::secmon::boot { void MakePageTable(); + void UnmapPhysicalIdentityMapping(); void InitializeColdBoot(); diff --git a/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere2/program/source/boot/secmon_boot_cache.cpp index ad0649c3e..f2d1f8e22 100644 --- a/exosphere2/program/source/boot/secmon_boot_cache.cpp +++ b/exosphere2/program/source/boot/secmon_boot_cache.cpp @@ -14,9 +14,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "secmon_boot_cache.hpp" namespace ams::secmon::boot { - /* TODO */ + #include "../secmon_cache_impl.inc" } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_cache.hpp b/exosphere2/program/source/boot/secmon_boot_cache.hpp new file mode 100644 index 000000000..60d5b4208 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_cache.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon::boot { + + #include "../secmon_cache.inc" + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index a9488e44e..537c78c5a 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -15,6 +15,7 @@ */ #include <exosphere.hpp> #include "secmon_boot.hpp" +#include "secmon_boot_cache.hpp" #include "secmon_boot_functions.hpp" namespace ams::secmon::boot { @@ -104,4 +105,30 @@ namespace ams::secmon::boot { SYSCTR0_REG_BITS_ENUM(CNTCR_EN, ENABLE)); } + void WriteGpuCarveoutMagicNumbers() { + /* Define the magic numbers. */ + constexpr u32 GpuMagicNumber = 0xC0EDBBCC; + constexpr u32 SkuInfo = 0x83; + constexpr u32 HdcpMicroCodeVersion = 0x2; + constexpr u32 ChipIdErista = 0x210; + constexpr u32 ChipIdMariko = 0x214; + + /* Get our pointers. */ + u32 *gpu_magic = MemoryRegionDramGpuCarveout.GetEndPointer<u32>() - (0x004 / sizeof(*gpu_magic)); + u32 *tsec_magic = MemoryRegionDramGpuCarveout.GetEndPointer<u32>() - (0x100 / sizeof(*tsec_magic)); + + /* Write the gpu magic number. */ + gpu_magic[0] = GpuMagicNumber; + + /* Write the tsec magic numbers. */ + tsec_magic[0] = SkuInfo; + tsec_magic[1] = HdcpMicroCodeVersion; + tsec_magic[2] = (false /* TODO: IsMariko */) ? ChipIdMariko : ChipIdErista; + + /* Flush the magic numbers. */ + hw::FlushDataCache(gpu_magic, 1 * sizeof(u32)); + hw::FlushDataCache(tsec_magic, 3 * sizeof(u32)); + hw::DataSynchronizationBarrierInnerShareable(); + } + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp index 83ac091db..97b5a105d 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -27,4 +27,6 @@ namespace ams::secmon::boot { void EnableTsc(u64 initial_tsc_value); + void WriteGpuCarveoutMagicNumbers(); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp index 06b411f87..c9255a529 100644 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -15,6 +15,7 @@ */ #include <exosphere.hpp> #include "secmon_boot.hpp" +#include "secmon_boot_cache.hpp" #include "../secmon_setup.hpp" #include "../secmon_key_storage.hpp" @@ -308,6 +309,25 @@ namespace ams::secmon::boot { } + namespace { + + using namespace ams::mmu; + + constexpr void UnmapPhysicalIdentityMappingImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Invalidate the L3 entries for the tzram and iram boot code regions. */ + InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize()); + InvalidateL3Entries(l3, MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetSize()); + + /* Unmap the L2 entries corresponding to those L3 entries. */ + InvalidateL2Entries(l2, MemoryRegionPhysicalIramL2.GetAddress(), MemoryRegionPhysicalIramL2.GetSize()); + InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize()); + + /* Unmap the L1 entry corresponding to to those L2 entries. */ + InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); + } + + } + void InitializeColdBoot() { /* Ensure that the system counters are valid. */ ValidateSystemCounters(); @@ -334,4 +354,16 @@ namespace ams::secmon::boot { SaveSecurityEngineAesKeySlotTestVector(); } -} \ No newline at end of file + void UnmapPhysicalIdentityMapping() { + /* Get the tables. */ + u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + + /* Unmap. */ + UnmapPhysicalIdentityMappingImpl(l1, l2_l3, l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::boot::EnsureMappingConsistency(); + } + +} diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 93b9f9c93..0b09e8ecc 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -89,6 +89,36 @@ namespace ams::secmon { secmon::boot::EnableTsc(bc.data.GetInitialTscValue() & TscMask); } + + /* Wait for NX Bootloader to initialize DRAM. */ + secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_InitializedDram); + + /* Secure the PMC and MC. */ + secmon::SetupPmcAndMcSecure(); + + /* Copy warmboot to dram. */ + { + /* Define warmboot extents. */ + const void * const src = MemoryRegionPhysicalIramWarmbootBin.GetPointer(); + void * const dst = MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware.GetPointer(); + const size_t size = MemoryRegionPhysicalIramWarmbootBin.GetSize(); + + /* Ensure we copy the correct data. */ + hw::FlushDataCache(src, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy warmboot.bin to its secure dram location. */ + std::memcpy(dst, src, size); + } + + /* Unmap the identity mapping. */ + secmon::boot::UnmapPhysicalIdentityMapping(); + + /* Setup the GPU carveout's magic numbers. */ + secmon::boot::WriteGpuCarveoutMagicNumbers(); + + /* Wait for NX bootloader to load Package2. */ + secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedPackage2); } } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp index dfde30d16..876bc54c0 100644 --- a/exosphere2/program/source/boot/secmon_make_page_table.cpp +++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp @@ -144,4 +144,4 @@ namespace ams::secmon::boot { MakePageTablesImpl(l1, l2_l3, l2_l3); } -} \ No newline at end of file +} diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc index 1ee25f79a..ea57c0f4e 100644 --- a/exosphere2/program/source/secmon_cache.inc +++ b/exosphere2/program/source/secmon_cache.inc @@ -19,4 +19,4 @@ void InvalidateEntireDataCache(); void EnsureMappingConsistency(); void EnsureMappingConsistency(uintptr_t address); -void EnsureInstructionConsistency(); \ No newline at end of file +void EnsureInstructionConsistency(); diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 7bb95aa63..296f66d68 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -840,6 +840,16 @@ namespace ams::secmon { } + void SetupPmcAndMcSecure() { + /* Set the PMC secure. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE)); + + /* Set the MC secure. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE)); + } + void SetupCpuCoreContext() { /* Get the tsc frequency. */ const u32 tsc_frequency = reg::Read(MemoryRegionVirtualDeviceSysCtr0.GetAddress() + SYSCTR0_CNTFID0); diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp index 623e10e74..c3c468fee 100644 --- a/exosphere2/program/source/secmon_setup.hpp +++ b/exosphere2/program/source/secmon_setup.hpp @@ -31,6 +31,8 @@ namespace ams::secmon { void SetupSocSecurity(); void SetupSocProtections(); + void SetupPmcAndMcSecure(); + void Setup1(); void SaveSecurityEngineAesKeySlotTestVector(); diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index 1e25108c5..d521efadc 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -74,6 +74,9 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionPhysical = MemoryRegion(UINT64_C( 0x40000000), 1_GB); constexpr inline const MemoryRegion MemoryRegionDram = MemoryRegion(UINT64_C( 0x80000000), 2_GB); + constexpr inline const MemoryRegion MemoryRegionDramGpuCarveout = MemoryRegion(UINT64_C(0x80020000), UINT64_C(0x40000)); + static_assert(MemoryRegionDram.Contains(MemoryRegionDramGpuCarveout)); + constexpr inline const MemoryRegion MemoryRegionDramDefaultKernelCarveout = MemoryRegion(UINT64_C(0x80060000), UINT64_C(0x1FFE0000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramDefaultKernelCarveout)); @@ -274,6 +277,7 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(UINT64_C(0x7C010800), 0xD800); constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(UINT64_C(0x40032000), 0xC000); - constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootConfig = MemoryRegion(UINT64_C(0x4003F800), 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramWarmbootBin = MemoryRegion(UINT64_C(0x4003E000), 0x17F0); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootConfig = MemoryRegion(UINT64_C(0x4003F800), 0x400); } diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp index d7e7fd497..650067eb6 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp @@ -16,7 +16,7 @@ #pragma once #include <vapours.hpp> -#define SYSCTR0_CNTCR (0x00C) +#define SYSCTR0_CNTCR (0x000) #define SYSCTR0_CNTCV0 (0x008) #define SYSCTR0_CNTCV1 (0x00C) #define SYSCTR0_CNTFID0 (0x020) From f391354415d4052704a29984f5f7399c7055156a Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 11:40:29 -0700 Subject: [PATCH 045/118] exo2: implement through package2 decryption --- .../program/source/boot/secmon_boot.hpp | 3 + .../source/boot/secmon_boot_functions.cpp | 86 ++++++++++++++++++- .../source/boot/secmon_boot_functions.hpp | 8 +- .../program/source/boot/secmon_main.cpp | 26 ++++++ .../program/source/boot/secmon_package2.cpp | 34 +++++++- libraries/libexosphere/include/exosphere.hpp | 1 + .../exosphere/pkg1/pkg1_boot_config.hpp | 4 + .../libexosphere/include/exosphere/pkg2.hpp | 81 +++++++++++++++++ .../include/exosphere/se/se_aes.hpp | 4 +- .../exosphere/secmon/secmon_memory_layout.hpp | 3 + libraries/libexosphere/source/se/se_aes.cpp | 78 +++++++++++++++-- 11 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 libraries/libexosphere/include/exosphere/pkg2.hpp diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp index 1214b8850..eb5138bcd 100644 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -29,4 +29,7 @@ namespace ams::secmon::boot { bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size); bool VerifyBootConfigEcid(const pkg1::BootConfig &bc); + bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size); + void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 537c78c5a..1cce9cc1e 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "../secmon_error.hpp" #include "secmon_boot.hpp" #include "secmon_boot_cache.hpp" #include "secmon_boot_functions.hpp" @@ -43,6 +44,55 @@ namespace ams::secmon::boot { 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D }; + constinit const u8 Package2RsaPublicModulusProduction[se::RsaSize] = { + 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, + 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, + 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, + 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, + 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, + 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, + 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, + 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, + 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, + 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, + 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, + 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, + 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, + 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, + 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, + 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F + }; + + constinit const u8 Package2RsaPublicModulusDevelopment[se::RsaSize] = { + 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, + 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, + 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, + 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, + 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, + 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, + 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, + 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, + 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, + 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, + 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, + 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, + 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, + 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, + 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, + 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B + }; + + constinit const u8 Package2AesKey[] { + 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 + }; + + } + + void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message) { + if (!verify_result) { + secmon::SetError(error_info); + AMS_ABORT(message); + } } void ClearIram() { @@ -131,4 +181,38 @@ namespace ams::secmon::boot { hw::DataSynchronizationBarrierInnerShareable(); } -} \ No newline at end of file + void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header) { + /* Check for all-zeroes signature. */ + bool is_decrypted = header.signature[0] == 0; + is_decrypted &= crypto::IsSameBytes(header.signature, header.signature + 1, sizeof(header.signature) - 1); + + /* Check for valid magic. */ + is_decrypted &= crypto::IsSameBytes(header.meta.magic, pkg2::Package2Meta::Magic::String, sizeof(header.meta.magic)); + + /* Set the setting in boot config. */ + secmon::impl::GetBootConfigStorage()->signed_data.SetPackage2Decrypted(is_decrypted); + } + + void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify) { + if (pkg1::IsProductionForPublicKey()) { + CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, Package2RsaPublicModulusProduction, util::size(Package2RsaPublicModulusProduction)), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed"); + } else if (verify) { + CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, Package2RsaPublicModulusDevelopment, util::size(Package2RsaPublicModulusDevelopment)), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed"); + } + } + + void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted) { + if (encrypted) { + constexpr int IvSize = 0x10; + + /* Decrypt the header. */ + DecryptPackage2(dst, sizeof(*dst), std::addressof(src), sizeof(src), Package2AesKey, util::size(Package2AesKey), std::addressof(src), IvSize, src.GetKeyGeneration()); + + /* Copy back the iv, which encodes encrypted metadata. */ + std::memcpy(dst, std::addressof(src), IvSize); + } else { + std::memcpy(dst, std::addressof(src), sizeof(*dst)); + } + } + +} diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp index 97b5a105d..7d3d05ebc 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -29,4 +29,10 @@ namespace ams::secmon::boot { void WriteGpuCarveoutMagicNumbers(); -} \ No newline at end of file + void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header); + void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify); + void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted); + + void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message); + +} diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 0b09e8ecc..6e66c54a9 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -119,6 +119,32 @@ namespace ams::secmon { /* Wait for NX bootloader to load Package2. */ secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedPackage2); + + /* Parse and decrypt the package2 header. */ + pkg2::Package2Meta pkg2_meta; + const uintptr_t pkg2_segments_start = MemoryRegionDramPackage2.GetAddress() + sizeof(pkg2::Package2Header); + { + /* Read the encrypred header. */ + pkg2::Package2Header encrypted_header; + + const auto *dram_header = MemoryRegionDramPackage2.GetPointer<pkg2::Package2Header>(); + hw::FlushDataCache(dram_header, sizeof(*dram_header)); + hw::DataSynchronizationBarrierInnerShareable(); + + std::memcpy(std::addressof(encrypted_header), dram_header, sizeof(encrypted_header)); + + /* Atmosphere extension: support plaintext package2, identified by all-zeroes signature and decrypted header. */ + secmon::boot::UpdateBootConfigForPackage2Header(encrypted_header); + + /* Verify the package2 header's signature. */ + secmon::boot::VerifyPackage2HeaderSignature(encrypted_header, !bc.signed_data.IsPackage2SignatureVerificationDisabled()); + + /* Decrypt the package2 header. */ + secmon::boot::DecryptPackage2Header(std::addressof(pkg2_meta), encrypted_header.meta, !bc.signed_data.IsPackage2EncryptionDisabled()); + } + + /* TODO */ + AMS_UNUSED(pkg2_segments_start); } } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp index ad0649c3e..0543502ad 100644 --- a/exosphere2/program/source/boot/secmon_package2.cpp +++ b/exosphere2/program/source/boot/secmon_package2.cpp @@ -14,9 +14,39 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "../secmon_error.hpp" +#include "../secmon_key_storage.hpp" +#include "secmon_boot.hpp" namespace ams::secmon::boot { - /* TODO */ + bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size) { + return VerifySignature(header.signature, sizeof(header.signature), mod, mod_size, std::addressof(header.meta), sizeof(header.meta)); + } -} \ No newline at end of file + void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation) { + /* Ensure that the SE sees consistent data. */ + hw::FlushDataCache(key, key_size); + hw::FlushDataCache(src, src_size); + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Load the needed master key into the temporary keyslot. */ + secmon::LoadMasterKey(pkg1::AesKeySlot_Temporary, key_generation); + + /* Load the package2 key into the temporary keyslot. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, pkg1::AesKeySlot_Temporary, key, key_size); + + /* Decrypt the data. */ + se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Temporary, src, src_size, iv, iv_size); + + /* Clear the keyslot we just used. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); + + /* Ensure that the cpu sees consistent data. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + +} diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index b320a52e5..c6468ab62 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -25,6 +25,7 @@ #include <exosphere/gic.hpp> #include <exosphere/wdt.hpp> #include <exosphere/pkg1.hpp> +#include <exosphere/pkg2.hpp> #include <exosphere/tsec.hpp> #include <exosphere/se.hpp> #include <exosphere/flow.hpp> diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp index 8cef0c823..5d37aa2cd 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -122,6 +122,10 @@ namespace ams::pkg1 { constexpr bool IsProgramVerificationDisabled() const { return (this->flags1[0] & (1 << 0)) != 0; } + + constexpr void SetPackage2Decrypted(bool decrypted) { + this->flags |= decrypted ? 0x3 : 0x0; + } }; static_assert(util::is_pod<BootConfigSignedData>::value); static_assert(sizeof(BootConfigSignedData) == 0x100); diff --git a/libraries/libexosphere/include/exosphere/pkg2.hpp b/libraries/libexosphere/include/exosphere/pkg2.hpp new file mode 100644 index 000000000..8df2bc651 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg2.hpp @@ -0,0 +1,81 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pkg2 { + + constexpr inline size_t Package2SizeMax = 8_MB - 16_KB; + constexpr inline size_t SegmentAlignment = 4; + + constexpr inline int SegmentCount = 3; + + constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x10 in Nintendo's code. */ + constexpr inline int CurrentBootloaderVersion = 0xD; + + struct Package2Meta { + using Magic = util::FourCC<'P','K','2','1'>; + + u32 package2_size; + u8 key_generation; + u8 header_iv_remainder[11]; + u8 segment_iv[SegmentCount][0x10]; + u8 padding_40[0x10]; + u8 magic[4]; + u32 entrypoint; + u8 padding_58[4]; + u8 package2_version; + u8 bootloader_version; + u8 padding_5E[2]; + u32 segment_sizes[SegmentCount]; + u8 padding_6C[4]; + u32 segment_offsets[SegmentCount]; + u8 padding_7C[4]; + u8 segment_hashes[SegmentCount][crypto::Sha256Generator::HashSize]; + u8 padding_E0[0x20]; + + private: + static ALWAYS_INLINE u32 ReadWord(const void *ptr, int offset) { + return util::LoadLittleEndian(reinterpret_cast<const u32 *>(reinterpret_cast<uintptr_t>(ptr) + offset)); + } + public: + ALWAYS_INLINE u8 GetKeyGeneration() const { + return std::min<u8>(0, (this->key_generation ^ this->header_iv_remainder[1] ^ this->header_iv_remainder[2]) - 1); + } + + ALWAYS_INLINE u32 GetSize() const { + return this->package2_size ^ ReadWord(this->header_iv_remainder, 3) ^ ReadWord(this->header_iv_remainder, 7); + } + }; + static_assert(util::is_pod<Package2Meta>::value); + static_assert(sizeof(Package2Meta) == 0x100); + + struct Package2Header { + u8 signature[0x100]; + Package2Meta meta; + }; + static_assert(util::is_pod<Package2Header>::value); + static_assert(sizeof(Package2Header) == 0x200); + + struct StorageLayout { + u8 boot_config[16_KB]; + Package2Header package2_header; + u8 data[Package2SizeMax - sizeof(Package2Header)]; + }; + static_assert(util::is_pod<StorageLayout>::value); + static_assert(sizeof(StorageLayout) == 8_MB); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index c2873f115..a9bf3e67f 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -33,4 +33,6 @@ namespace ams::se { void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); -} \ No newline at end of file + void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index d521efadc..34e4be521 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -80,6 +80,9 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionDramDefaultKernelCarveout = MemoryRegion(UINT64_C(0x80060000), UINT64_C(0x1FFE0000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramDefaultKernelCarveout)); + constexpr inline const MemoryRegion MemoryRegionDramPackage2 = MemoryRegion(UINT64_C(0xA9800000), UINT64_C(0x07FC0000)); + static_assert(MemoryRegionDram.Contains(MemoryRegionDramPackage2)); + constexpr inline const MemoryRegion MemoryRegionPhysicalIram = MemoryRegion(UINT64_C(0x40000000), 0x40000); constexpr inline const MemoryRegion MemoryRegionPhysicalTzram = MemoryRegion(UINT64_C(0x7C010000), 0x10000); static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalIram)); diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index f9aac61b8..138e18cfb 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -33,13 +33,21 @@ namespace ams::se { MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF, }; - constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + constexpr inline u32 AesConfigCtr = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 1), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, LINEAR_CTR), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) { reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), @@ -69,6 +77,16 @@ namespace ams::se { // reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); // } + void SetCounter(volatile SecurityEngineRegisters *SE, const void *ctr) { + const u32 *ctr_32 = reinterpret_cast<const u32 *>(ctr); + + /* Copy the input ctr to the linear CTR registers. */ + reg::Write(SE->SE_CRYPTO_LINEAR_CTR[0], util::LoadLittleEndian(ctr_32 + 0)); + reg::Write(SE->SE_CRYPTO_LINEAR_CTR[1], util::LoadLittleEndian(ctr_32 + 1)); + reg::Write(SE->SE_CRYPTO_LINEAR_CTR[2], util::LoadLittleEndian(ctr_32 + 2)); + reg::Write(SE->SE_CRYPTO_LINEAR_CTR[3], util::LoadLittleEndian(ctr_32 + 3)); + } + void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) { AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount); @@ -206,4 +224,50 @@ namespace ams::se { ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); } + void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + /* If nothing to do, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine how many full blocks we can operate on. */ + const size_t num_blocks = src_size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + const size_t fractional = src_size - aligned_size; + + /* Here Nintendo writes 1 to SE_SPARE. It's unclear why they do this, but we will do so as well. */ + SE->SE_SPARE = 0x1; + + /* Configure for AES-CTR encryption/decryption to memory. */ + SetConfig(SE, true, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, true, AesConfigCtr); + + /* Set the counter. */ + SetCounter(SE, iv); + + /* Process as many aligned blocks as we can. */ + if (aligned_size > 0) { + /* Configure the engine to process the right number of blocks. */ + SetBlockCount(SE, num_blocks); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size); + + /* Synchronize around this point. */ + hw::DataSynchronizationBarrierInnerShareable(); + } + + /* Process a single block to output. */ + if (fractional > 0 && dst_size > aligned_size) { + const size_t copy_size = std::min(fractional, dst_size - aligned_size); + + ExecuteOperationSingleBlock(SE, static_cast<u8 *>(dst) + aligned_size, copy_size, static_cast<const u8 *>(src) + aligned_size, fractional); + } + } + } From 87bdc46bebee6b6c2b89005ad4cf8610c63dec3b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 13:27:53 -0700 Subject: [PATCH 046/118] exo2: implement rest of main other than SetupSocProtections --- .../program/source/boot/secmon_boot.hpp | 7 ++ .../source/boot/secmon_boot_functions.cpp | 87 +++++---------- .../source/boot/secmon_boot_functions.hpp | 4 + .../source/boot/secmon_boot_key_data.cpp | 82 ++++++++++++++ .../source/boot/secmon_boot_key_data.hpp | 26 +++++ .../program/source/boot/secmon_boot_setup.cpp | 17 +++ .../program/source/boot/secmon_main.cpp | 48 ++++++++- .../program/source/boot/secmon_package2.cpp | 100 ++++++++++++++++++ exosphere2/program/source/secmon_cache.cpp | 23 ++++ .../secmon_key_data.cpp => secmon_cache.hpp} | 5 +- .../program/source/secmon_cache_impl.inc | 2 +- exosphere2/program/source/secmon_misc.cpp | 13 ++- exosphere2/program/source/secmon_misc.hpp | 3 + exosphere2/program/source/secmon_setup.cpp | 16 ++- .../libexosphere/include/exosphere/pkg2.hpp | 14 +-- .../exosphere/secmon/secmon_memory_layout.hpp | 3 + libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_overlap.hpp | 35 ++++++ 18 files changed, 408 insertions(+), 78 deletions(-) create mode 100644 exosphere2/program/source/boot/secmon_boot_key_data.cpp create mode 100644 exosphere2/program/source/boot/secmon_boot_key_data.hpp create mode 100644 exosphere2/program/source/secmon_cache.cpp rename exosphere2/program/source/{boot/secmon_key_data.cpp => secmon_cache.hpp} (90%) create mode 100644 libraries/libvapours/include/vapours/util/util_overlap.hpp diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp index eb5138bcd..469aa8884 100644 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -20,6 +20,7 @@ namespace ams::secmon::boot { void MakePageTable(); void UnmapPhysicalIdentityMapping(); + void UnmapDram(); void InitializeColdBoot(); @@ -29,7 +30,13 @@ namespace ams::secmon::boot { bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size); bool VerifyBootConfigEcid(const pkg1::BootConfig &bc); + void CalculatePackage2Hash(se::Sha256Hash *dst, const pkg2::Package2Meta &meta, uintptr_t package2_start); + bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size); void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation); + bool VerifyPackage2Meta(const pkg2::Package2Meta &meta); + bool VerifyPackage2Version(const pkg2::Package2Meta &meta); + bool VerifyPackage2Payloads(const pkg2::Package2Meta &meta, uintptr_t payload_address); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 1cce9cc1e..6c33be920 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -18,6 +18,7 @@ #include "secmon_boot.hpp" #include "secmon_boot_cache.hpp" #include "secmon_boot_functions.hpp" +#include "secmon_boot_key_data.hpp" namespace ams::secmon::boot { @@ -25,66 +26,9 @@ namespace ams::secmon::boot { constexpr inline uintptr_t SYSCTR0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); - constinit const u8 BootConfigRsaPublicModulus[se::RsaSize] = { - 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, - 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, - 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, - 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, - 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, - 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, - 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, - 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, - 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, - 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, - 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, - 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, - 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, - 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, - 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, - 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D - }; - - constinit const u8 Package2RsaPublicModulusProduction[se::RsaSize] = { - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - }; - - constinit const u8 Package2RsaPublicModulusDevelopment[se::RsaSize] = { - 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, - 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, - 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, - 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, - 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, - 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, - 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, - 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, - 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, - 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, - 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, - 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, - 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, - 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, - 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, - 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B - }; - - constinit const u8 Package2AesKey[] { - 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 - }; + NOINLINE void DecryptPayload(uintptr_t dst, uintptr_t src, size_t size, const void *iv, size_t iv_size, u8 key_generation) { + secmon::boot::DecryptPackage2(reinterpret_cast<void *>(dst), size, reinterpret_cast<void *>(src), size, Package2AesKey, util::size(Package2AesKey), iv, iv_size, key_generation); + } } @@ -215,4 +159,27 @@ namespace ams::secmon::boot { } } + void VerifyPackage2Header(const pkg2::Package2Meta &meta) { + /* Validate the metadata. */ + CheckVerifyResult(VerifyPackage2Meta(meta), pkg1::ErrorInfo_InvalidPackage2Meta, "package2 meta verification failed"); + + /* Validate the version. */ + CheckVerifyResult(VerifyPackage2Version(meta), pkg1::ErrorInfo_InvalidPackage2Version, "package2 version verification failed"); + } + + void DecryptAndLoadPackage2Payloads(uintptr_t dst, const pkg2::Package2Meta &meta, uintptr_t src, bool encrypted) { + /* Get the key generation for crypto. */ + const u8 key_generation = meta.GetKeyGeneration(); + /* Decrypt or load each payload in order. */ + for (int i = 0; i < pkg2::PayloadCount; ++i) { + if (encrypted) { + DecryptPayload(dst + meta.payload_offsets[i], src, meta.payload_sizes[i], meta.payload_ivs[i], sizeof(meta.payload_ivs[i]), key_generation); + } else { + std::memcpy(reinterpret_cast<void *>(dst + meta.payload_offsets[i]), reinterpret_cast<void *>(src), meta.payload_sizes[i]); + } + + src += meta.payload_sizes[i]; + } + } + } diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp index 7d3d05ebc..c03a04b64 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -33,6 +33,10 @@ namespace ams::secmon::boot { void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify); void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted); + void VerifyPackage2Header(const pkg2::Package2Meta &meta); + + void DecryptAndLoadPackage2Payloads(uintptr_t dst, const pkg2::Package2Meta &meta, uintptr_t src, bool encrypted); + void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message); } diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.cpp b/exosphere2/program/source/boot/secmon_boot_key_data.cpp new file mode 100644 index 000000000..e3e74dd6e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_key_data.cpp @@ -0,0 +1,82 @@ +/* + * 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_boot_key_data.hpp" + +namespace ams::secmon::boot { + + constinit const u8 BootConfigRsaPublicModulus[se::RsaSize] = { + 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, + 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, + 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, + 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, + 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, + 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, + 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, + 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, + 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, + 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, + 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, + 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, + 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, + 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, + 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, + 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D + }; + + constinit const u8 Package2RsaPublicModulusProduction[se::RsaSize] = { + 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, + 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, + 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, + 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, + 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, + 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, + 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, + 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, + 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, + 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, + 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, + 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, + 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, + 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, + 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, + 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F + }; + + constinit const u8 Package2RsaPublicModulusDevelopment[se::RsaSize] = { + 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, + 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, + 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, + 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, + 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, + 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, + 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, + 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, + 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, + 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, + 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, + 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, + 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, + 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, + 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, + 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B + }; + + constinit const u8 Package2AesKey[] { + 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 + }; + +} diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.hpp b/exosphere2/program/source/boot/secmon_boot_key_data.hpp new file mode 100644 index 000000000..06513a5d3 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_key_data.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon::boot { + + extern const u8 BootConfigRsaPublicModulus[se::RsaSize]; + extern const u8 Package2RsaPublicModulusDevelopment[se::RsaSize]; + extern const u8 Package2RsaPublicModulusProduction[se::RsaSize]; + extern const u8 Package2AesKey[se::AesBlockSize]; + +} diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp index c9255a529..03a3be3f8 100644 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -326,6 +326,11 @@ namespace ams::secmon::boot { InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); } + constexpr void UnmapDramImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Unmap the L1 entry corresponding to to the Dram entries. */ + InvalidateL1Entries(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize()); + } + } void InitializeColdBoot() { @@ -366,4 +371,16 @@ namespace ams::secmon::boot { secmon::boot::EnsureMappingConsistency(); } + void UnmapDram() { + /* Get the tables. */ + u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + + /* Unmap. */ + UnmapDramImpl(l1, l2_l3, l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::boot::EnsureMappingConsistency(); + } + } diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 6e66c54a9..773feb347 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -17,11 +17,19 @@ #include "secmon_boot.hpp" #include "secmon_boot_functions.hpp" #include "../smc/secmon_random_cache.hpp" -#include "../secmon_setup.hpp" +#include "../secmon_cache.hpp" +#include "../secmon_cpu_context.hpp" #include "../secmon_misc.hpp" +#include "../secmon_setup.hpp" namespace ams::secmon { + namespace { + + constexpr inline const uintptr_t Package2LoadAddress = MemoryRegionDramPackage2Payloads.GetAddress(); + + } + void Main() { /* Set library register addresses. */ /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ @@ -122,7 +130,7 @@ namespace ams::secmon { /* Parse and decrypt the package2 header. */ pkg2::Package2Meta pkg2_meta; - const uintptr_t pkg2_segments_start = MemoryRegionDramPackage2.GetAddress() + sizeof(pkg2::Package2Header); + const uintptr_t pkg2_payloads_start = MemoryRegionDramPackage2.GetAddress() + sizeof(pkg2::Package2Header); { /* Read the encrypred header. */ pkg2::Package2Header encrypted_header; @@ -143,8 +151,38 @@ namespace ams::secmon { secmon::boot::DecryptPackage2Header(std::addressof(pkg2_meta), encrypted_header.meta, !bc.signed_data.IsPackage2EncryptionDisabled()); } - /* TODO */ - AMS_UNUSED(pkg2_segments_start); + /* Verify the package2 header. */ + secmon::boot::VerifyPackage2Header(pkg2_meta); + + /* Save the package2 hash if in recovery boot. */ + if (secmon::IsRecoveryBoot()) { + se::Sha256Hash hash; + secmon::boot::CalculatePackage2Hash(std::addressof(hash), pkg2_meta, MemoryRegionDramPackage2.GetAddress()); + secmon::SetPackage2Hash(hash); + } + + /* Verify the package2 payloads. */ + secmon::boot::CheckVerifyResult(secmon::boot::VerifyPackage2Payloads(pkg2_meta, pkg2_payloads_start), pkg1::ErrorInfo_InvalidPackage2Payload, "package2 payload verification failed"); + + /* Decrypt/Move the package2 payloads to the right places. */ + secmon::boot::DecryptAndLoadPackage2Payloads(Package2LoadAddress, pkg2_meta, pkg2_payloads_start, !bc.signed_data.IsPackage2EncryptionDisabled()); + + /* Ensure that the CPU sees correct package2 data. */ + secmon::FlushEntireDataCache(); + secmon::EnsureInstructionConsistency(); + + /* Set the core's entrypoint and argument. */ + secmon::SetEntryContext(0, Package2LoadAddress + pkg2_meta.entrypoint, 0); + + /* Unmap DRAM. */ + secmon::boot::UnmapDram(); + + /* Wait for NX bootloader to be done. */ + secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_Done); + + /* Perform final initialization. */ + secmon::SetupSocProtections(); + secmon::SetupCpuSErrorDebug(); } -} \ No newline at end of file +} diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp index 0543502ad..3a440b373 100644 --- a/exosphere2/program/source/boot/secmon_package2.cpp +++ b/exosphere2/program/source/boot/secmon_package2.cpp @@ -17,9 +17,23 @@ #include "../secmon_error.hpp" #include "../secmon_key_storage.hpp" #include "secmon_boot.hpp" +#include "secmon_boot_key_data.hpp" namespace ams::secmon::boot { + void CalculatePackage2Hash(se::Sha256Hash *dst, const pkg2::Package2Meta &meta, uintptr_t package2_start) { + /* Determine the region to hash. */ + const void *data = reinterpret_cast<const void *>(package2_start); + const size_t size = meta.GetSize(); + + /* Flush to ensure the SE sees the correct data. */ + hw::FlushDataCache(data, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + se::CalculateSha256(dst, data, size); + } + bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size) { return VerifySignature(header.signature, sizeof(header.signature), mod, mod_size, std::addressof(header.meta), sizeof(header.meta)); } @@ -49,4 +63,90 @@ namespace ams::secmon::boot { hw::DataSynchronizationBarrierInnerShareable(); } + bool VerifyPackage2Meta(const pkg2::Package2Meta &meta) { + /* Get the obfuscated metadata. */ + const size_t size = meta.GetSize(); + const u8 key_generation = meta.GetKeyGeneration(); + + /* Check that size is big enough for the header. */ + if (size <= sizeof(pkg2::Package2Header)) { + return false; + } + + /* Check that the size isn't larger than what we allow. */ + if (size > pkg2::Package2SizeMax) { + return false; + } + + /* Check that the key generation is one that we can use. */ + static_assert(pkg1::KeyGeneration_Count == 11); + if (key_generation >= pkg1::KeyGeneration_Count) { + return false; + } + + /* Check the magic number. */ + if (!crypto::IsSameBytes(meta.magic, pkg2::Package2Meta::Magic::String, sizeof(meta.magic))) { + return false; + } + + /* Check the payload alignments. */ + if ((meta.entrypoint % pkg2::PayloadAlignment) != 0) { + return false; + } + + for (int i = 0; i < pkg2::PayloadCount; ++i) { + if ((meta.payload_sizes[i] % pkg2::PayloadAlignment) != 0) { + return false; + } + } + + /* Check that the sizes sum to the total. */ + if (size != sizeof(pkg2::Package2Header) + meta.payload_sizes[0] + meta.payload_sizes[1] + meta.payload_sizes[2]) { + return false; + } + + /* Check that the payloads do not overflow. */ + for (int i = 0; i < pkg2::PayloadCount; ++i) { + if (meta.payload_offsets[i] > meta.payload_offsets[i] + meta.payload_sizes[i]) { + return false; + } + } + + /* Verify that no payloads overlap. */ + for (int i = 0; i < pkg2::PayloadCount - 1; ++i) { + for (int j = i + 1; j < pkg2::PayloadCount; ++j) { + if (util::HasOverlap(meta.payload_offsets[i], meta.payload_sizes[i], meta.payload_offsets[j], meta.payload_sizes[j])) { + return false; + } + } + } + + /* Check whether any payload contains the entrypoint. */ + for (int i = 0; i < pkg2::PayloadCount; ++i) { + if (util::Contains(meta.payload_offsets[i], meta.payload_sizes[i], meta.entrypoint)) { + return true; + } + } + + /* No payload contains the entrypoint, so we're not valid. */ + return false; + } + + bool VerifyPackage2Version(const pkg2::Package2Meta &meta) { + return meta.bootloader_version <= pkg2::CurrentBootloaderVersion && meta.package2_version >= pkg2::MinimumValidDataVersion; + } + + bool VerifyPackage2Payloads(const pkg2::Package2Meta &meta, uintptr_t payload_address) { + /* Verify hashes match for all payloads. */ + for (int i = 0; i < pkg2::PayloadCount; ++i) { + if (!VerifyHash(meta.payload_hashes[i], payload_address, meta.payload_sizes[i])) { + return false; + } + + payload_address += meta.payload_sizes[i]; + } + + return true; + } + } diff --git a/exosphere2/program/source/secmon_cache.cpp b/exosphere2/program/source/secmon_cache.cpp new file mode 100644 index 000000000..02fd04383 --- /dev/null +++ b/exosphere2/program/source/secmon_cache.cpp @@ -0,0 +1,23 @@ +/* + * 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_cache.hpp" + +namespace ams::secmon { + + #include "secmon_cache_impl.inc" + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_key_data.cpp b/exosphere2/program/source/secmon_cache.hpp similarity index 90% rename from exosphere2/program/source/boot/secmon_key_data.cpp rename to exosphere2/program/source/secmon_cache.hpp index ad0649c3e..3050c38cf 100644 --- a/exosphere2/program/source/boot/secmon_key_data.cpp +++ b/exosphere2/program/source/secmon_cache.hpp @@ -13,10 +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/>. */ +#pragma once #include <exosphere.hpp> -namespace ams::secmon::boot { +namespace ams::secmon { - /* TODO */ + #include "secmon_cache.inc" } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc index c486c8b4d..6ef2fe4bc 100644 --- a/exosphere2/program/source/secmon_cache_impl.inc +++ b/exosphere2/program/source/secmon_cache_impl.inc @@ -129,4 +129,4 @@ void EnsureInstructionConsistency() { ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InstructionSynchronizationBarrier(); -} \ No newline at end of file +} diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp index 8f7a1c500..d0981d771 100644 --- a/exosphere2/program/source/secmon_misc.cpp +++ b/exosphere2/program/source/secmon_misc.cpp @@ -20,7 +20,8 @@ namespace ams::secmon { namespace { - pkg1::BctParameters g_bct_params; + constinit pkg1::BctParameters g_bct_params = {}; + constinit se::Sha256Hash g_package2_hash = {}; } @@ -43,4 +44,12 @@ namespace ams::secmon { return dbg_auth.Get<hw::DbgAuthStatusEl1::Nsid>() == 3; } -} \ No newline at end of file + void GetPackage2Hash(se::Sha256Hash *out) { + *out = g_package2_hash; + } + + void SetPackage2Hash(const se::Sha256Hash &hash) { + g_package2_hash = hash; + } + +} diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp index b26641919..e8b5922ff 100644 --- a/exosphere2/program/source/secmon_misc.hpp +++ b/exosphere2/program/source/secmon_misc.hpp @@ -26,4 +26,7 @@ namespace ams::secmon { bool IsJtagEnabled(); + void GetPackage2Hash(se::Sha256Hash *out); + void SetPackage2Hash(const se::Sha256Hash &hash); + } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 296f66d68..71c8fa190 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -837,7 +837,7 @@ namespace ams::secmon { } void SetupSocProtections() { - + /* TODO */ } void SetupPmcAndMcSecure() { @@ -877,7 +877,21 @@ namespace ams::secmon { } void SetupCpuSErrorDebug() { + /* Get whether we should enable SError debug. */ + const auto &bc_data = secmon::GetBootConfig().data; + const bool enabled = bc_data.IsDevelopmentFunctionEnabled() && bc_data.IsSErrorDebugEnabled(); + /* Get and set scr_el3. */ + { + util::BitPack32 scr; + HW_CPU_GET_SCR_EL3(scr); + + scr.Set<hw::ScrEl3::Ea>(enabled ? 0 : 1); + HW_CPU_SET_SCR_EL3(scr); + } + + /* Prevent reordering instructions around this call. */ + hw::InstructionSynchronizationBarrier(); } } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg2.hpp b/libraries/libexosphere/include/exosphere/pkg2.hpp index 8df2bc651..9fa9425d1 100644 --- a/libraries/libexosphere/include/exosphere/pkg2.hpp +++ b/libraries/libexosphere/include/exosphere/pkg2.hpp @@ -19,9 +19,9 @@ namespace ams::pkg2 { constexpr inline size_t Package2SizeMax = 8_MB - 16_KB; - constexpr inline size_t SegmentAlignment = 4; + constexpr inline size_t PayloadAlignment = 4; - constexpr inline int SegmentCount = 3; + constexpr inline int PayloadCount = 3; constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x10 in Nintendo's code. */ constexpr inline int CurrentBootloaderVersion = 0xD; @@ -32,7 +32,7 @@ namespace ams::pkg2 { u32 package2_size; u8 key_generation; u8 header_iv_remainder[11]; - u8 segment_iv[SegmentCount][0x10]; + u8 payload_ivs[PayloadCount][0x10]; u8 padding_40[0x10]; u8 magic[4]; u32 entrypoint; @@ -40,11 +40,11 @@ namespace ams::pkg2 { u8 package2_version; u8 bootloader_version; u8 padding_5E[2]; - u32 segment_sizes[SegmentCount]; + u32 payload_sizes[PayloadCount]; u8 padding_6C[4]; - u32 segment_offsets[SegmentCount]; + u32 payload_offsets[PayloadCount]; u8 padding_7C[4]; - u8 segment_hashes[SegmentCount][crypto::Sha256Generator::HashSize]; + u8 payload_hashes[PayloadCount][crypto::Sha256Generator::HashSize]; u8 padding_E0[0x20]; private: @@ -53,7 +53,7 @@ namespace ams::pkg2 { } public: ALWAYS_INLINE u8 GetKeyGeneration() const { - return std::min<u8>(0, (this->key_generation ^ this->header_iv_remainder[1] ^ this->header_iv_remainder[2]) - 1); + return static_cast<u8>(std::max<s32>(0, static_cast<s32>(this->key_generation ^ this->header_iv_remainder[1] ^ this->header_iv_remainder[2]) - 1)); } ALWAYS_INLINE u32 GetSize() const { diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index 34e4be521..ffe9625e7 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -80,6 +80,9 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionDramDefaultKernelCarveout = MemoryRegion(UINT64_C(0x80060000), UINT64_C(0x1FFE0000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramDefaultKernelCarveout)); + constexpr inline const MemoryRegion MemoryRegionDramPackage2Payloads = MemoryRegion(MemoryRegionDram.GetAddress(), 8_MB); + static_assert(MemoryRegionDram.Contains(MemoryRegionDramPackage2Payloads)); + constexpr inline const MemoryRegion MemoryRegionDramPackage2 = MemoryRegion(UINT64_C(0xA9800000), UINT64_C(0x07FC0000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramPackage2)); diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index c10fa5d37..0d60a3ea7 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -36,5 +36,6 @@ #include <vapours/util/util_tinymt.hpp> #include <vapours/util/util_uuid.hpp> #include <vapours/util/util_bounded_map.hpp> +#include <vapours/util/util_overlap.hpp> #include <vapours/util/util_string_util.hpp> #include <vapours/util/util_variadic.hpp> diff --git a/libraries/libvapours/include/vapours/util/util_overlap.hpp b/libraries/libvapours/include/vapours/util/util_overlap.hpp new file mode 100644 index 000000000..a1bf7d2df --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_overlap.hpp @@ -0,0 +1,35 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> + +namespace ams::util { + + constexpr inline bool HasOverlap(uintptr_t addr0, size_t size0, uintptr_t addr1, size_t size1) { + if (addr0 <= addr1) { + return addr1 < addr0 + size0; + } else { + return addr0 < addr1 + size1; + } + } + + constexpr inline bool Contains(uintptr_t addr, size_t size, uintptr_t ptr) { + return (addr <= ptr) && (ptr < addr + size); + } + +} From dc6abf9f6805bc26b8d3c7781bf22781f977c4ca Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 17:14:46 -0700 Subject: [PATCH 047/118] exo2: Implement the rest of main/return-to-el1 --- exosphere2/program/program.ld | 7 +- .../program/source/boot/secmon_boot_setup.cpp | 8 +- exosphere2/program/source/secmon_map.cpp | 52 +++++ exosphere2/program/source/secmon_map.hpp | 23 ++ exosphere2/program/source/secmon_setup.cpp | 219 +++++++++++++++++- .../program/source/secmon_setup_warm.cpp | 4 + exosphere2/program/source/secmon_stack_warm.s | 21 ++ .../program/source/secmon_start_virtual.s | 127 +++++++++- exosphere2/program/source/secmon_start_warm.s | 110 +++++++++ .../libexosphere/include/exosphere/clkrst.hpp | 1 + .../libexosphere/include/exosphere/tegra.hpp | 1 + .../include/exosphere/tegra/tegra_clkrst.hpp | 87 +++++++ .../include/exosphere/tegra/tegra_evp.hpp | 11 +- .../exosphere/tegra/tegra_flow_ctlr.hpp | 4 + .../include/exosphere/tegra/tegra_mc.hpp | 25 ++ .../libexosphere/source/actmon/actmon_api.cpp | 58 ++++- .../source/actmon/actmon_registers.hpp | 52 +++++ .../libexosphere/source/clkrst/clkrst_api.cpp | 13 +- .../source/clkrst/clkrst_registers.hpp | 71 ------ 19 files changed, 801 insertions(+), 93 deletions(-) create mode 100644 exosphere2/program/source/secmon_map.cpp create mode 100644 exosphere2/program/source/secmon_map.hpp create mode 100644 exosphere2/program/source/secmon_stack_warm.s create mode 100644 exosphere2/program/source/secmon_start_warm.s create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp create mode 100644 libraries/libexosphere/source/actmon/actmon_registers.hpp delete mode 100644 libraries/libexosphere/source/clkrst/clkrst_registers.hpp diff --git a/exosphere2/program/program.ld b/exosphere2/program/program.ld index 61e8720dc..b451dd5c7 100644 --- a/exosphere2/program/program.ld +++ b/exosphere2/program/program.ld @@ -36,15 +36,12 @@ SECTIONS { KEEP (*(.crt0 .crt0.*)) KEEP (secmon_crt0_cpp.o(.text*)) - KEEP (secmon_boot_cache.o(.text*)) KEEP (secmon_make_page_table.o(.text*)) *(.crt0.rodata*) secmon_crt0_cpp.o(.rodata*) - secmon_boot_cache.o(.rodata*) secmon_make_page_table.o(.rodata*) *(.crt0.data*) secmon_crt0_cpp.o(.data*) - secmon_boot_cache.o(.data*) secmon_make_page_table.o(.data*) . = ALIGN(8); } >iram_boot_code AT>glob @@ -95,18 +92,21 @@ SECTIONS { KEEP(secmon_main.o(.text*)) KEEP(secmon_boot_functions.o(.text*)) + KEEP (secmon_boot_cache.o(.text*)) KEEP(secmon_boot_config.o(.text*)) KEEP(secmon_boot_setup.o(.text*)) KEEP(secmon_package2.o(.text*)) KEEP(secmon_key_data.o(.text*)) secmon_main.o(.rodata*) secmon_boot_functions.o(.rodata*) + secmon_boot_cache.o(.rodata*) secmon_boot_config.o(.rodata*) secmon_boot_setup.o(.rodata*) secmon_package2.o(.rodata*) secmon_key_data.o(.rodata*) secmon_main.o(.data*) secmon_boot_functions.o(.data*) + secmon_boot_cache.o(.data*) secmon_boot_config.o(.data*) secmon_boot_setup.o(.data*) secmon_package2.o(.data*) @@ -119,6 +119,7 @@ SECTIONS __boot_bss_start__ = ABSOLUTE(.); secmon_main.o(.bss* COMMON) secmon_boot_functions.o(.bss* COMMON) + secmon_boot_cache.o(.bss* COMMON) secmon_boot_config.o(.bss* COMMON) secmon_boot_setup.o(.bss* COMMON) secmon_package2.o(.bss* COMMON) diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp index 03a3be3f8..d0daa30d9 100644 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -361,8 +361,8 @@ namespace ams::secmon::boot { void UnmapPhysicalIdentityMapping() { /* Get the tables. */ - u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); - u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); /* Unmap. */ UnmapPhysicalIdentityMappingImpl(l1, l2_l3, l2_l3); @@ -373,8 +373,8 @@ namespace ams::secmon::boot { void UnmapDram() { /* Get the tables. */ - u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); - u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); /* Unmap. */ UnmapDramImpl(l1, l2_l3, l2_l3); diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp new file mode 100644 index 000000000..a2c1d0088 --- /dev/null +++ b/exosphere2/program/source/secmon_map.cpp @@ -0,0 +1,52 @@ +/* + * 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_cache.hpp" +#include "secmon_map.hpp" + +namespace ams::secmon { + + namespace { + + using namespace ams::mmu; + + constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { + /* Unmap the L3 entries corresponding to the boot code. */ + InvalidateL3Entries(l3, boot_code, boot_code_size); + } + + } + + void UnmapBootCode() { + /* Get the tables. */ + u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + /* Get the boot code region. */ + const uintptr_t boot_code = MemoryRegionVirtualTzramBootCode.GetAddress(); + const size_t boot_code_size = MemoryRegionVirtualTzramBootCode.GetSize(); + + /* Clear the boot code. */ + util::ClearMemory(reinterpret_cast<void *>(boot_code), boot_code_size); + + /* Unmap. */ + UnmapBootCodeImpl(l1, l2_l3, l2_l3, boot_code, boot_code_size); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(); + } + +} diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp new file mode 100644 index 000000000..9d0aac678 --- /dev/null +++ b/exosphere2/program/source/secmon_map.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 71c8fa190..fa791e079 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -18,6 +18,7 @@ #include "secmon_error.hpp" #include "secmon_cpu_context.hpp" #include "secmon_interrupt_handler.hpp" +#include "secmon_misc.hpp" namespace ams::secmon { @@ -28,6 +29,8 @@ namespace ams::secmon { constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionVirtualDeviceFlowController.GetAddress(); constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc.GetAddress(); constexpr inline const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress(); + constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress(); + constexpr inline const uintptr_t CLK_RST = secmon::MemoryRegionVirtualDeviceClkRst.GetAddress(); alignas(8) constinit u8 g_se_aes_key_slot_test_vector[se::AesBlockSize] = {}; @@ -621,7 +624,7 @@ namespace ams::secmon { reg::Read (MC + MC_SMMU_TLB_CONFIG); /* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */ - reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Write(MC + MC_SMMU_TLB_FLUSH, 0); reg::Read (MC + MC_SMMU_TLB_CONFIG); /* Enable the SMMU, and read TLB_CONFIG to ensure the enable takes. */ @@ -722,6 +725,173 @@ namespace ams::secmon { hw::InstructionSynchronizationBarrier(); } + void SetupGpuCarveout() { + /* Configure carveout 2. */ + reg::Write(MC + MC_SECURITY_CARVEOUT2_BOM, static_cast<u32>(MemoryRegionDramGpuCarveout.GetAddress() >> 0)); + reg::Write(MC + MC_SECURITY_CARVEOUT2_BOM_HI, static_cast<u32>(MemoryRegionDramGpuCarveout.GetAddress() >> BITSIZEOF(u32))); + reg::Write(MC + MC_SECURITY_CARVEOUT2_SIZE_128KB, MemoryRegionDramGpuCarveout.GetSize() / 128_KB); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2, MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSRD, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSWR, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS2_TSECSRD, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4, MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSRD2, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSWR2, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT2_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 2), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + } + + void DisableArc() { + /* Configure IRAM top/bottom to point to memory ends (disabling redirection). */ + reg::Write(MC + MC_IRAM_BOM, (~0u) & MC_IRAM_BOM_WRITE_MASK); + reg::Write(MC + MC_IRAM_TOM, ( 0u) & MC_IRAM_TOM_WRITE_MASK); + + /* Lock the IRAM aperture. */ + reg::Write(MC + MC_IRAM_REG_CTRL, MC_REG_BITS_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, DISABLED)); + + /* Disable the ARC clock gate override. */ + reg::ReadWrite(CLK_RST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, CLK_RST_REG_BITS_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, OFF)); + + /* Rea IRAM REG CTRL to make sure our writes take. */ + reg::Read(MC + MC_IRAM_REG_CTRL); + } + + void FinalizeCarveoutSecureScratchRegisters() { + /* Define carveout scratch values. */ + constexpr uintptr_t WarmbootCarveoutAddress = MemoryRegionDram.GetAddress(); + constexpr size_t WarmbootCarveoutSize = 128_KB; + + #define MC_ENABLE_CLIENT_ACCESS(INDEX, WHICH) MC_REG_BITS_ENUM(CLIENT_ACCESS##INDEX##_##WHICH, ENABLE) + + constexpr u32 WarmbootCarveoutClientAccess0 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(0, AVPCARM7R), + MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBSLVR)); + + constexpr u32 WarmbootCarveoutClientAccess1 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(1, AVPCARM7W)); + + #undef MC_ENABLE_CLIENT_ACCESS + + constexpr u32 WarmbootCarveoutForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE), + MC_REG_BITS_ENUM(CLIENT_ACCESS0_PPCSAHBSLVR, ENABLE)); + + constexpr u32 WarmbootCarveoutForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + + constexpr u32 WarmbootCarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, UNLOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + + /* Save the carveout values into secure scratch. */ + + /* Save MC_SECURITY_CARVEOUT4_BOM. */ + reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH51, REG_BITS_VALUE( 0, 15, WarmbootCarveoutAddress >> 17)); + + /* Save MC_SECURITY_CARVEOUT4_BOM_HI. */ + reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH16, REG_BITS_VALUE(30, 2, WarmbootCarveoutAddress >> 32)); + + /* Save MC_SECURITY_CARVEOUT4_SIZE_128KB. */ + reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH55, REG_BITS_VALUE(12, 12, WarmbootCarveoutSize / 128_KB)); + + /* Save MC_SECURITY_CARVEOUT4_CLIENT_ACCESS. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH74, WarmbootCarveoutClientAccess0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH75, WarmbootCarveoutClientAccess1); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH76, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH77, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH78, 0); + + /* Save MC_SECURITY_CARVEOUT4_FORCE_INTERNAL_ACCESS. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH99, WarmbootCarveoutForceInternalAccess0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH100, WarmbootCarveoutForceInternalAccess1); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH101, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH102, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH103, 0); + + /* Save MC_SECURITY_CARVEOUT4_CFG0. */ + reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH39, REG_BITS_VALUE(0, 27, WarmbootCarveoutConfig)); + } + + void EnableBpmpSmmu() { + /* Define the ASID contents. */ + constexpr int BpmpAsid = 1; + constexpr uintptr_t BpmpAsidPde = MemoryRegionPhysicalDeviceSecurityEngine.GetAddress(); + + /* Configure the ASID. */ + reg::Write(MC + MC_SMMU_PTB_ASID, MC_REG_BITS_VALUE(SMMU_PTB_ASID_CURRENT_ASID, BpmpAsid)); + + reg::Write(MC + MC_SMMU_PTB_DATA, MC_REG_BITS_VALUE(SMMU_PTB_DATA_ASID_PDE_BASE, BpmpAsidPde / 4_KB), + MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_NONSECURE, DISABLE), + MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_WRITABLE, DISABLE), + MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_READABLE, DISABLE)); + + /* Configure the BPMP and PPCS1 to use the asid. */ + reg::Write(MC + MC_SMMU_AVPC_ASID, MC_REG_BITS_ENUM(SMMU_AVPC_ASID_AVPC_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_AVPC_ASID_AVPC_ASID, BpmpAsid)); + reg::Write(MC + MC_SMMU_PPCS1_ASID, MC_REG_BITS_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_PPCS1_ASID_PPCS1_ASID, BpmpAsid)); + + /* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_TLB_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + } + + void ActmonInterruptHandler() { + SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt); + AMS_ABORT("actmon observed bpmp wakeup"); + } + } void Setup1() { @@ -783,7 +953,7 @@ namespace ams::secmon { SetupSmmu(); /* Clear the cpu reset vector. */ - reg::Write(secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress() + EVP_CPU_RESET_VECTOR, 0); + reg::Write(EVP + EVP_CPU_RESET_VECTOR, 0); /* Configure the SB registers to our start address. */ constexpr u32 ResetVectorLow = static_cast<u32>((PhysicalTzramProgramResetVector >> 0)); @@ -836,8 +1006,51 @@ namespace ams::secmon { } } + void SetupSocSecurityWarmboot() { + /* ... */ + } + void SetupSocProtections() { - /* TODO */ + /* Setup the GPU carveout. */ + SetupGpuCarveout(); + + /* Disable the ARC. */ + DisableArc(); + + /* Finalize and lock the carveout scratch registers. */ + FinalizeCarveoutSecureScratchRegisters(); + pmc::LockSecureRegister(pmc::SecureRegister_Carveout); + + /* Clear all the BPMP exception vectors to a fixed value. */ + constexpr u32 BpmpExceptionVector = 0x7D000000; + reg::Write(EVP + EVP_COP_RESET_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_UNDEF_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_SWI_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_RSVD_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_IRQ_VECTOR, BpmpExceptionVector); + reg::Write(EVP + EVP_COP_FIQ_VECTOR, BpmpExceptionVector); + + /* Turn on the SMMU for the BPMP. */ + EnableBpmpSmmu(); + + /* Wait until the flow controller reports that the BPMP is halted. */ + while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP))) { + util::WaitMicroSeconds(1); + } + + /* If JTAG is disabled, disable JTAG. */ + if (!secmon::IsJtagEnabled()) { + reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, DISABLED)); + + /* If version is above 4.0.0, turn on the activity monitor to prevent booting up the bpmp. */ + if (GetTargetFirmware() >= TargetFirmware_4_0_0) { + clkrst::EnableActmonClock(); + actmon::StartMonitoringBpmp(ActmonInterruptHandler); + } + } } void SetupPmcAndMcSecure() { diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp index e0b661d3b..b463d321e 100644 --- a/exosphere2/program/source/secmon_setup_warm.cpp +++ b/exosphere2/program/source/secmon_setup_warm.cpp @@ -203,4 +203,8 @@ namespace ams::secmon { } } + void SetupSocDmaControllersCpuMemoryControllersEnableMmuWarmboot() { + /* TODO */ + } + } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_stack_warm.s b/exosphere2/program/source/secmon_stack_warm.s new file mode 100644 index 000000000..85dad852b --- /dev/null +++ b/exosphere2/program/source/secmon_stack_warm.s @@ -0,0 +1,21 @@ +/* + * 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/>. + */ + +.section .warmboot.data._ZN3ams6secmon23CommonWarmbootStackLockE, "aw", %progbits +.global _ZN3ams6secmon23CommonWarmbootStackLockE +_ZN3ams6secmon23CommonWarmbootStackLockE: + /* Define storage for the global common warmboot stack bakery lock. */ + .word 0 diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index e71744dad..8d918fbf3 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -15,7 +15,7 @@ */ .section .text._ZN3ams6secmon5StartEv, "ax", %progbits -.align 6 +.align 4 .global _ZN3ams6secmon5StartEv _ZN3ams6secmon5StartEv: /* Set SPSEL 1 stack pointer to the core 0 exception stack address. */ @@ -35,12 +35,102 @@ _ZN3ams6secmon5StartEv: bl _ZN3ams6secmon4MainEv /* Set the stack pointer to the core 3 exception stack address. */ - ldr x20, =0x1F01FB000 + ldr x20, =0x1F01F9000 mov sp, x20 - /* TODO: JumpToLowerExceptionLevel */ + /* Unmap the boot code region. */ + bl _ZN3ams6secmon13UnmapBootCodeEv + + /* Jump to lower exception level. */ + b _ZN3ams6secmon25JumpToLowerExceptionLevelEv + +.section .text._ZN3ams6secmon20StartWarmbootVirtualEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon20StartWarmbootVirtualEv +_ZN3ams6secmon20StartWarmbootVirtualEv: + /* Set the stack pointer to the shared warmboot stack address. */ + ldr x20, =0x1F01F67C0 + mov sp, x20 + + /* Perform final warmboot setup. */ + bl _ZN3ams6secmon24SetupSocSecurityWarmbootEv + + /* Jump to lower exception level. */ + b _ZN3ams6secmon25JumpToLowerExceptionLevelEv + +.section .text._ZN3ams6secmon25JumpToLowerExceptionLevelEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon25JumpToLowerExceptionLevelEv +_ZN3ams6secmon25JumpToLowerExceptionLevelEv: + /* Get the EntryContext. */ + sub sp, sp, #0x10 + mov x0, sp + bl _ZN3ams6secmon15GetEntryContextEPNS0_12EntryContextE + + /* Load the entrypoint and argument from the context. */ + ldr x19, [sp, #0x00] + ldr x0, [sp, #0x08] + + /* Set the exception return address. */ + msr elr_el3, x0 + + /* Get the core exception stack. */ + bl _ZN3ams6secmon28GetCoreExceptionStackVirtualEv + mov sp, x0 + + /* Release our exclusive access to the common warmboot stack. */ + bl _ZN3ams6secmon26ReleaseCommonWarmbootStackEv + + /* Configure SPSR_EL3. */ + mov x0, #0x3C5 + msr spsr_el3, x0 + + /* Set x0 to the entry argument. */ + mov x0, x19 + + /* Ensure instruction reordering doesn't happen around this point. */ + isb + + /* Return to lower level. */ + eret + + /* Infinite loop, though we should never get here. */ 1: b 1b +.section .text._ZN3ams6secmon28GetCoreExceptionStackVirtualEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon28GetCoreExceptionStackVirtualEv +_ZN3ams6secmon28GetCoreExceptionStackVirtualEv: + /* Get the current core id. */ + mrs x0, mpidr_el1 + and x0, x0, #3 + + /* Jump to the appropriate core's stack handler. */ + cmp x0, #3 + b.eq 3f + + cmp x0, #2 + b.eq 2f + + cmp x1, #1 + b.eq 1f + + /* cmp x0, #0 */ + /* b.eq 0f */ + + 0: + ldr x0, =0x1F01F6F00 + ret + 1: + ldr x0, =0x1F01F6F80 + ret + 2: + ldr x0, =0x1F01F7000 + ret + 3: + ldr x0, =0x1F01F9000 + ret + .section .text._ZN3ams6secmon25AcquireCommonSmcStackLockEv, "ax", %progbits .align 4 .global _ZN3ams6secmon25AcquireCommonSmcStackLockEv @@ -75,6 +165,37 @@ _ZN3ams6secmon25ReleaseCommonSmcStackLockEv: /* Return. */ ret + ret + +.section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon26ReleaseCommonWarmbootStackEv +_ZN3ams6secmon26ReleaseCommonWarmbootStackEv: + /* Get the virtual address of the lock. */ + ldr x0, =_ZN3ams6secmon23CommonWarmbootStackLockE + ldr x1, =(0x1F00C0000 - 0x07C012000) + add x1, x1, x0 + + /* Get the bakery value for our core. */ + mrs x0, mpidr_el1 + and x0, x0, #3 + ldrb w2, [x1, x0] + + /* Clear our ticket number. */ + and w2, w2, #(~0x7F) + strb w2, [x1, x0] + + /* Flush the cache. */ + dc civac, x1 + + /* Synchronize data for all cores. */ + dsb sy + + /* Send an event. */ + sev + + /* Return. */ + ret .section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits .global _ZN3ams6secmon18CommonSmcStackLockE diff --git a/exosphere2/program/source/secmon_start_warm.s b/exosphere2/program/source/secmon_start_warm.s new file mode 100644 index 000000000..84b9cf901 --- /dev/null +++ b/exosphere2/program/source/secmon_start_warm.s @@ -0,0 +1,110 @@ +/* + * 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 .warmboot.text.start, "ax", %progbits +.align 4 +.global _start_warm +_start_warm: + /* mask all interrupts */ + msr daifset, #0xF + + /* Fixup hardware erratum */ + ERRATUM_INVALIDATE_BTB_AT_BOOT + + /* Acquire exclusive access to the common warmboot stack. */ + + /* Set the stack pointer to the common warmboot stack address. */ + msr spsel, #1 + ldr x20, =0x1F01F67C0 + mov sp, x20 + + /* Perform warmboot setup. */ + bl _ZN3ams6secmon59SetupSocDmaControllersCpuMemoryControllersEnableMmuWarmbootEv + + /* Jump to the newly-mapped virtual address. */ + b _ZN3ams6secmon20StartWarmbootVirtualEv + + diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp index c26dfb118..2f0143977 100644 --- a/libraries/libexosphere/include/exosphere/clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -25,5 +25,6 @@ namespace ams::clkrst { void EnableUartAClock(); void EnableUartBClock(); void EnableUartCClock(); + void EnableActmonClock(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tegra.hpp b/libraries/libexosphere/include/exosphere/tegra.hpp index 370bcc3bc..a6c8a0dda 100644 --- a/libraries/libexosphere/include/exosphere/tegra.hpp +++ b/libraries/libexosphere/include/exosphere/tegra.hpp @@ -19,6 +19,7 @@ #include <exosphere/tegra/tegra_ahb_arbc.hpp> #include <exosphere/tegra/tegra_apb_misc.hpp> #include <exosphere/tegra/tegra_avp_cache.hpp> +#include <exosphere/tegra/tegra_clkrst.hpp> #include <exosphere/tegra/tegra_emc.hpp> #include <exosphere/tegra/tegra_evp.hpp> #include <exosphere/tegra/tegra_flow_ctlr.hpp> diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp new file mode 100644 index 000000000..035b28594 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -0,0 +1,87 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +/* Clock source enums. */ +#define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME) +#define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE) +#define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM) +#define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + +#define CLK_RST_CONTROLLER_RST_SOURCE (0x000) + +#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) + +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD (0x3A4) + +DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); + +/* RST_DEVICES */ +#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004) +#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008) +#define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C) +#define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C) +#define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4) +#define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358) +#define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C) + +/* CLK_OUT_ENB */ +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360) +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) + +/* CLK_SOURCE */ +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) +#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3e8) + +/* CLK_ENB_*_INDEX */ +#define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) +#define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) +#define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) +#define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_INDEX (0x17) + +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, 19, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSEC_CLK_OVR_ON, 20, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ISPB_CLK_OVR_ON, 22, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TZRAM_CLK_OVR_ON, 23, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_QSPI_CLK_OVR_ON, 24, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_A9AVP_CLK_OVR_ON, 26, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_MPCORE_MSELECT_CLK_OVR_ON, 27, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC1_LEGACY_TMCLK_OVR_ON, 28, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON); +DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON); + +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp index 27d61f7cf..24135720c 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp @@ -16,4 +16,13 @@ #pragma once #include <vapours.hpp> -#define EVP_CPU_RESET_VECTOR (0x100) +#define EVP_CPU_RESET_VECTOR (0x100) + +#define EVP_COP_RESET_VECTOR (0x200) +#define EVP_COP_UNDEF_VECTOR (0x204) +#define EVP_COP_SWI_VECTOR (0x208) +#define EVP_COP_PREFETCH_ABORT_VECTOR (0x20C) +#define EVP_COP_DATA_ABORT_VECTOR (0x210) +#define EVP_COP_RSVD_VECTOR (0x214) +#define EVP_COP_IRQ_VECTOR (0x218) +#define EVP_COP_FIQ_VECTOR (0x21C) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index eca5115ff..d30493330 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -29,6 +29,7 @@ #define FLOW_CTLR_HALT_CPU1_EVENTS (0x014) #define FLOW_CTLR_HALT_CPU2_EVENTS (0x01C) #define FLOW_CTLR_HALT_CPU3_EVENTS (0x024) +#define FLOW_CTLR_HALT_COP_EVENTS (0x004) #define FLOW_CTLR_CC4_CORE0_CTRL (0x06C) #define FLOW_CTLR_CC4_CORE1_CTRL (0x070) @@ -46,6 +47,9 @@ #define DEFINE_FLOW_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) #define DEFINE_FLOW_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) +DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, ENABLED, DISABLED); +DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MODE_RUN_AND_INT, FLOW_MODE_STOP, FLOW_MODE_STOP_AND_INT, FLOW_MODE_STOP_UNTIL_IRQ, FLOW_MODE_STOP_UNTIL_IRQ_AND_INT, FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ, RESERVED7); + DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, 0, FAST, SLOW); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp index b97b5c631..7f309daf1 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp @@ -29,6 +29,9 @@ #define MC_SMMU_TLB_FLUSH (0x030) #define MC_SMMU_PTC_FLUSH (0x034) +#define MC_SMMU_AVPC_ASID (0x23C) +#define MC_SMMU_PPCS1_ASID (0x298) + #define MC_SECURITY_CFG0 (0x070) #define MC_SECURITY_CFG1 (0x074) #define MC_SECURITY_CFG3 (0x9BC) @@ -48,6 +51,10 @@ #define MC_SMMU_ASID_SECURITY_6 (0x9f0) #define MC_SMMU_ASID_SECURITY_7 (0x9f4) +#define MC_IRAM_BOM (0x65c) +#define MC_IRAM_TOM (0x660) +#define MC_IRAM_REG_CTRL (0x964) + #define MC_SEC_CARVEOUT_BOM (0x670) #define MC_SEC_CARVEOUT_SIZE_MB (0x674) #define MC_SEC_CARVEOUT_REG_CTRL (0x678) @@ -160,6 +167,19 @@ DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0, 7); DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 24, 4); DEFINE_MC_REG_BIT_ENUM(SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, 29, DISABLE, ENABLE); +DEFINE_MC_REG(SMMU_PTB_ASID_CURRENT_ASID, 0, 7); + +DEFINE_MC_REG(SMMU_PTB_DATA_ASID_PDE_BASE, 0, 22); +DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_NONSECURE, 29, DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_WRITABLE, 30, DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(SMMU_PTB_DATA_ASID_READABLE, 31, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_AVPC_ASID_AVPC_ASID, 0, 7); +DEFINE_MC_REG_BIT_ENUM(SMMU_AVPC_ASID_AVPC_SMMU_ENABLE, 31, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_PPCS1_ASID_PPCS1_ASID, 0, 7); +DEFINE_MC_REG_BIT_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, 31, DISABLE, ENABLE); + DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_0, 0, NONSECURE, SECURE); DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, 1, NONSECURE, SECURE); DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, 2, NONSECURE, SECURE); @@ -327,3 +347,8 @@ DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECRDB, (134 - (MC_CLIENT_ACCESS_N DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECWRB, (135 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSRD2, (136 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSWR2, (137 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); + +constexpr inline u32 MC_IRAM_BOM_WRITE_MASK = 0xFFFFF000u; +constexpr inline u32 MC_IRAM_TOM_WRITE_MASK = 0xFFFFF000u; + +DEFINE_MC_REG_BIT_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, 0, ENABLED, DISABLED); diff --git a/libraries/libexosphere/source/actmon/actmon_api.cpp b/libraries/libexosphere/source/actmon/actmon_api.cpp index ee7843cbf..f6c6764eb 100644 --- a/libraries/libexosphere/source/actmon/actmon_api.cpp +++ b/libraries/libexosphere/source/actmon/actmon_api.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "actmon_registers.hpp" namespace ams::actmon { @@ -21,6 +22,8 @@ namespace ams::actmon { constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceActivityMonitor.GetAddress(); + constinit InterruptHandler g_interrupt_handler = nullptr; + } void SetRegisterAddress(uintptr_t address) { @@ -28,15 +31,64 @@ namespace ams::actmon { } void HandleInterrupt() { - /* TODO */ + /* Get the registers. */ + const uintptr_t ACTMON = g_register_address; + + /* Disable the actmon interrupt. */ + reg::Write(ACTMON + ACTMON_COP_CTRL, ACTMON_REG_BITS_ENUM(COP_CTRL_ENB, DISABLE)); + + /* Update the interrupt status. */ + reg::Write(ACTMON + ACTMON_COP_INTR_STATUS, reg::Read(ACTMON + ACTMON_COP_INTR_STATUS)); + + /* Invoke the handler. */ + if (g_interrupt_handler != nullptr) { + g_interrupt_handler(); + g_interrupt_handler = nullptr; + } } void StartMonitoringBpmp(InterruptHandler handler) { - /* TODO */ + /* Get the registers. */ + const uintptr_t ACTMON = g_register_address; + + /* Configure the activity monitor to poll once per microsecond. */ + reg::Write(ACTMON + ACTMON_GLB_PERIOD_CTRL, ACTMON_REG_BITS_ENUM (GLB_PERIOD_CTRL_SOURCE, USEC), + ACTMON_REG_BITS_VALUE(GLB_PERIOD_CTRL_SAMPLE_PERIOD, 0)); + + /* Configure the activity monitor to generate an interrupt the first time the event occurs. */ + reg::Write(ACTMON + ACTMON_COP_UPPER_WMARK, 0); + + /* Set the interrupt handler. */ + g_interrupt_handler = handler; + + /* Configure the activity monitor to generate events whenever the bpmp is woken up. */ + reg::Write(ACTMON + ACTMON_COP_CTRL, ACTMON_REG_BITS_ENUM (COP_CTRL_ENB, ENABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_CONSECUTIVE_ABOVE_WMARK_EN, ENABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_CONSECUTIVE_BELOW_WMARK_EN, DISABLE), + ACTMON_REG_BITS_VALUE(COP_CTRL_ABOVE_WMARK_NUM, 0), + ACTMON_REG_BITS_VALUE(COP_CTRL_BELOW_WMARK_NUM, 0), + ACTMON_REG_BITS_ENUM (COP_CTRL_WHEN_OVERFLOW_EN, DISABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_AVG_ABOVE_WMARK_EN, DISABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_AVG_BELOW_WMARK_EN, DISABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_AT_END_EN, DISABLE), + ACTMON_REG_BITS_ENUM (COP_CTRL_ENB_PERIODIC, ENABLE)); + + /* Read the activity monitor control register to make sure our configuration takes. */ + reg::Read(ACTMON + ACTMON_COP_CTRL); } void StopMonitoringBpmp() { - /* TODO */ + /* Get the registers. */ + const uintptr_t ACTMON = g_register_address; + + /* Disable the actmon interrupt. */ + reg::Write(ACTMON + ACTMON_COP_CTRL, ACTMON_REG_BITS_ENUM(COP_CTRL_ENB, DISABLE)); + + /* Update the interrupt status. */ + reg::Write(ACTMON + ACTMON_COP_INTR_STATUS, reg::Read(ACTMON + ACTMON_COP_INTR_STATUS)); + + /* Clear the interrupt handler. */ + g_interrupt_handler = nullptr; } } \ No newline at end of file diff --git a/libraries/libexosphere/source/actmon/actmon_registers.hpp b/libraries/libexosphere/source/actmon/actmon_registers.hpp new file mode 100644 index 000000000..7cfb3cdc1 --- /dev/null +++ b/libraries/libexosphere/source/actmon/actmon_registers.hpp @@ -0,0 +1,52 @@ +/* + * 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> + +namespace ams::actmon { + + #define ACTMON_GLB_PERIOD_CTRL (0x004) + #define ACTMON_COP_CTRL (0x0C0) + #define ACTMON_COP_UPPER_WMARK (0x0C4) + #define ACTMON_COP_INTR_STATUS (0x0E4) + + /* Actmon source enums. */ + #define ACTMON_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (ACTMON, NAME) + #define ACTMON_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (ACTMON, NAME, VALUE) + #define ACTMON_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (ACTMON, NAME, ENUM) + #define ACTMON_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(ACTMON, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_ACTMON_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (ACTMON, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_ACTMON_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (ACTMON, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_ACTMON_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (ACTMON, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_ACTMON_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(ACTMON, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_ACTMON_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (ACTMON, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_ACTMON_REG(GLB_PERIOD_CTRL_SAMPLE_PERIOD, 0, 8); + DEFINE_ACTMON_REG_BIT_ENUM(GLB_PERIOD_CTRL_SOURCE, 8, MSEC, USEC); + + DEFINE_ACTMON_REG(COP_CTRL_K_VAL, 10, 3); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_ENB_PERIODIC, 18, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_AT_END_EN, 19, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_AVG_BELOW_WMARK_EN, 20, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_AVG_ABOVE_WMARK_EN, 21, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_WHEN_OVERFLOW_EN, 22, DISABLE, ENABLE); + DEFINE_ACTMON_REG(COP_CTRL_BELOW_WMARK_NUM, 23, 3); + DEFINE_ACTMON_REG(COP_CTRL_ABOVE_WMARK_NUM, 26, 3); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_CONSECUTIVE_BELOW_WMARK_EN, 29, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_CONSECUTIVE_ABOVE_WMARK_EN, 30, DISABLE, ENABLE); + DEFINE_ACTMON_REG_BIT_ENUM(COP_CTRL_ENB, 31, DISABLE, ENABLE); + +} diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp index c2de3028e..76601b6eb 100644 --- a/libraries/libexosphere/source/clkrst/clkrst_api.cpp +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> -#include "clkrst_registers.hpp" namespace ams::clkrst { @@ -68,9 +67,10 @@ namespace ams::clkrst { .clk_div = _DIV_, \ } - DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); - DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); - DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(ActmonClock, V, ACTMON, CLK_M, 0); } @@ -94,5 +94,8 @@ namespace ams::clkrst { EnableClock(UartAClock); } + void EnableActmonClock() { + EnableClock(ActmonClock); + } -} \ No newline at end of file +} diff --git a/libraries/libexosphere/source/clkrst/clkrst_registers.hpp b/libraries/libexosphere/source/clkrst/clkrst_registers.hpp deleted file mode 100644 index 5d5cd0874..000000000 --- a/libraries/libexosphere/source/clkrst/clkrst_registers.hpp +++ /dev/null @@ -1,71 +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 <exosphere.hpp> - -namespace ams::clkrst { - - /* Clock source enums. */ - #define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME) - #define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE) - #define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM) - #define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) - - #define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__) - #define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE) - #define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) - #define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) - #define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) - - - #define CLK_RST_CONTROLLER_RST_SOURCE (0x000) - - #define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) - - DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); - - /* RST_DEVICES */ - #define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004) - #define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008) - #define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C) - #define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C) - #define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4) - #define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358) - #define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C) - - /* CLK_OUT_ENB */ - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360) - #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) - - /* CLK_SOURCE */ - #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) - #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) - #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) - - /* CLK_ENB_*_INDEX */ - #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) - #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) - #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) - - DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) - DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) - DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) - -} From 81846fa5c314db35bc68b0bacccad61102454f80 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Tue, 12 May 2020 21:51:26 -0700 Subject: [PATCH 048/118] exo2: implement warmboot through start of virtual exec --- .../program/source/secmon_setup_warm.cpp | 78 ++++++++++++- exosphere2/program/source/secmon_start_warm.s | 104 +++++++++++++++++- 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp index b463d321e..560359339 100644 --- a/exosphere2/program/source/secmon_setup_warm.cpp +++ b/exosphere2/program/source/secmon_setup_warm.cpp @@ -26,8 +26,12 @@ namespace ams::secmon { namespace { + constexpr inline uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + using namespace ams::mmu; + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwCode, MemoryAttributeIndexNormal); + void SetupCpuCommonControllers() { /* Set cpuactlr_el1. */ { @@ -146,6 +150,69 @@ namespace ams::secmon { hw::InstructionSynchronizationBarrier(); } + bool IsExitLp0() { + return reg::Read(MC + MC_SECURITY_CFG3) == 0; + } + + constexpr void AddPhysicalTzramIdentityMappingImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Define extents. */ + const uintptr_t start_address = MemoryRegionPhysicalTzram.GetAddress(); + const size_t size = MemoryRegionPhysicalTzram.GetSize(); + const uintptr_t end_address = start_address + size; + + /* Flush cache for the L3 page table entries. */ + { + const uintptr_t start = GetL3EntryIndex(start_address); + const uintptr_t end = GetL3EntryIndex(end_address); + for (uintptr_t i = start; i < end; i += hw::DataCacheLineSize / sizeof(*l3)) { + if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l3 + i); } + } + } + + /* Flush cache for the L2 page table entry. */ + if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l2 + GetL2EntryIndex(start_address)); } + + /* Flush cache for the L1 page table entry. */ + if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l1 + GetL1EntryIndex(start_address)); } + + /* Add the L3 mappings. */ + SetL3BlockEntry(l3, start_address, start_address, size, MappingAttributesEl3SecureRwCode); + + /* Add the L2 entry for the physical tzram region. */ + SetL2TableEntry(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + + /* Add the L1 entry for the physical region. */ + SetL1TableEntry(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + static_assert(GetL1EntryIndex(MemoryRegionPhysical.GetAddress()) == 1); + + /* Invalidate the data cache for the L3 page table entries. */ + { + const uintptr_t start = GetL3EntryIndex(start_address); + const uintptr_t end = GetL3EntryIndex(end_address); + for (uintptr_t i = start; i < end; i += hw::DataCacheLineSize / sizeof(*l3)) { + if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l3 + i); } + } + } + + /* Flush cache for the L2 page table entry. */ + if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l2 + GetL2EntryIndex(start_address)); } + + /* Flush cache for the L1 page table entry. */ + if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l1 + GetL1EntryIndex(start_address)); } + } + + void AddPhysicalTzramIdentityMapping() { + /* Get page table extents. */ + u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>(); + + /* Add the mapping. */ + AddPhysicalTzramIdentityMappingImpl(l1, l2_l3, l2_l3); + + /* Ensure that mappings are consistent. */ + setup::EnsureMappingConsistency(); + } + } void SetupCpuMemoryControllersEnableMmu() { @@ -204,7 +271,16 @@ namespace ams::secmon { } void SetupSocDmaControllersCpuMemoryControllersEnableMmuWarmboot() { - /* TODO */ + /* If this is being called from lp0 exit, we want to setup the soc dma controllers. */ + if (IsExitLp0()) { + SetupSocDmaControllers(); + } + + /* Add a physical TZRAM identity map. */ + AddPhysicalTzramIdentityMapping(); + + /* Initialize cpu memory controllers and the MMU. */ + SetupCpuMemoryControllersEnableMmu(); } } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_start_warm.s b/exosphere2/program/source/secmon_start_warm.s index 84b9cf901..74eaa530e 100644 --- a/exosphere2/program/source/secmon_start_warm.s +++ b/exosphere2/program/source/secmon_start_warm.s @@ -95,10 +95,11 @@ _start_warm: ERRATUM_INVALIDATE_BTB_AT_BOOT /* Acquire exclusive access to the common warmboot stack. */ + bl _ZN3ams6secmon26AcquireCommonWarmbootStackEv /* Set the stack pointer to the common warmboot stack address. */ msr spsel, #1 - ldr x20, =0x1F01F67C0 + ldr x20, =0x7C0107C0 mov sp, x20 /* Perform warmboot setup. */ @@ -108,3 +109,104 @@ _start_warm: b _ZN3ams6secmon20StartWarmbootVirtualEv +/* void ams::secmon::AcquireCommonWarmbootStack() { */ +/* NOTE: This implements critical section enter via https://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm */ +/* This algorithm is used because the MMU is not awake yet, so exclusive load/store instructions are not usable. */ +/* NOTE: Nintendo attempted to implement this algorithm themselves, but did not really understand how it works. */ +/* They use the same ticket number for all cores; this can lead to starvation and other problems. */ +.section .warmboot.text._ZN3ams6secmon26AcquireCommonWarmbootStackEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon26AcquireCommonWarmbootStackEv +_ZN3ams6secmon26AcquireCommonWarmbootStackEv: + /* BakeryLock *lock = std::addressof(secmon::CommonWarmBootStackLock); */ + ldr x0, =_ZN3ams6secmon23CommonWarmbootStackLockE + + /* const u32 id = GetCurrentCoreId(); */ + mrs x8, mpidr_el1 + and x8, x8, #3 + + /* lock->customers[id].is_entering = true; */ + ldrb w2, [x0, x8] + orr w2, w2, #~0x7F + strb w2, [x0, x8] + + /* const u8 ticket_0 = lock->customers[0].ticket_number; */ + ldrb w4, [x0, #0] + and w4, w4, #0x7F + + /* const u8 ticket_1 = lock->customers[1].ticket_number; */ + ldrb w5, [x0, #1] + and w5, w5, #0x7F + + /* const u8 ticket_2 = lock->customers[2].ticket_number; */ + ldrb w6, [x0, #2] + and w6, w6, #0x7F + + /* const u8 ticket_3 = lock->customers[3].ticket_number; */ + ldrb w7, [x0, #3] + and w7, w7, #0x7F + + /* u8 biggest_ticket = std::max(std::max(ticket_0, ticket_1), std::max(ticket_2, ticket_3)) */ + cmp w4, w5 + csel w2, w4, w5, hi + cmp w6, w7 + csel w3, w6, w7, hi + cmp w2, w3 + csel w2, w2, w3, hi + + /* NOTE: The biggest a ticket can ever be is 4, so the general increment is safe and 7-bit increment is not needed. */ + /* lock->customers[id] = { .is_entering = false, .ticket_number = ++biggest_ticket }; */ + add w2, w2, #1 + strb w2, [x0, x8] + + /* Ensure instructions aren't reordered around this point. */ + /* hw::DataSynchronizationBarrier(); */ + dsb sy + + /* hw::SendEvent(); */ + sev + + /* for (unsigned int i = 0; i < 4; ++i) { */ + mov w3, #0 +1: + /* hw::SendEventLocal(); */ + sevl + + /* do { */ +2: + /* hw::WaitForEvent(); */ + wfe + /* while (lock->customers[i].is_entering); */ + ldrb w4, [x0, x3] + tbnz w4, #7, 2b + + /* u8 their_ticket; */ + + /* hw::SendEventLocal(); */ + sevl + + /* do { */ +2: + /* hw::WaitForEvent(); */ + wfe + /* their_ticket = lock->customers[i].ticket_number; */ + ldrb w4, [x0, x3] + ands w4, w4, #0x7F + /* if (their_ticket == 0) { break; } */ + b.eq 3f + /* while ((their_ticket > my_ticket) || (their_ticket == my_ticket && id > i)); */ + cmp w2, w4 + b.hi 2b + ccmp w8, w3, #0, eq + b.hi 2b + + /* } */ +3: + add w3, w3, #1 + cmp w3, #4 + b.ne 1b + + /* hw::DataMemoryBarrier(); */ + dmb sy + + ret From 97ab282351f83e508f921ac20a8f1b18f9c0b15d Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 10:37:50 -0700 Subject: [PATCH 049/118] exo2: update configitem names to match wiki --- .../program/source/smc/secmon_smc_info.hpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp index 1d41950ff..8a0e3c5d5 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -21,23 +21,23 @@ namespace ams::secmon::smc { enum class ConfigItem : u32 { /* Standard config items. */ - DisableProgramVerification = 1, - DramId = 2, - SecurityEngineIrqNumber = 3, - FuseVersion = 4, - HardwareType = 5, - IsRetail = 6, - IsRecoveryBoot = 7, - DeviceId = 8, - BootReason = 9, - MemoryMode = 10, - IsDebugMode = 11, - KernelConfiguration = 12, - IsChargerHiZModeEnabled = 13, - IsQuest = 14, - RegulatorType = 15, - DeviceUniqueKeyGeneration = 16, - Package2Hash = 17, + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineInterruptNumber = 3, + FuseVersion = 4, + HardwareType = 5, + HardwareState = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDevelopmentFunctionEnabled = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + IsQuest = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, + Package2Hash = 17, /* Extension config items for exosphere. */ ExosphereApiVersion = 65000, From ad664daea52ed2c46021e9e9234cc5f710416294 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 10:56:07 -0700 Subject: [PATCH 050/118] exo2: implement remainder of warmboot tz code --- .../program/source/boot/secmon_main.cpp | 4 +- exosphere2/program/source/secmon_map.cpp | 23 +++ exosphere2/program/source/secmon_map.hpp | 2 +- exosphere2/program/source/secmon_setup.cpp | 89 ++++++++- .../smc/secmon_smc_power_management.cpp | 14 ++ .../smc/secmon_smc_power_management.hpp | 3 + .../program/source/smc/secmon_smc_se_lock.cpp | 41 ++++ .../program/source/smc/secmon_smc_se_lock.hpp | 26 +++ libraries/libexosphere/include/exosphere.hpp | 2 + .../include/exosphere/charger.hpp | 25 +++ .../libexosphere/include/exosphere/clkrst.hpp | 3 + .../libexosphere/include/exosphere/log.hpp | 4 + .../libexosphere/include/exosphere/pinmux.hpp | 29 +++ .../include/exosphere/tegra/tegra_clkrst.hpp | 15 +- .../libexosphere/include/exosphere/uart.hpp | 6 +- .../source/charger/charger_api.cpp | 55 ++++++ .../libexosphere/source/clkrst/clkrst_api.cpp | 28 ++- libraries/libexosphere/source/log/log_api.cpp | 37 ++++ .../libexosphere/source/pinmux/pinmux_api.cpp | 181 ++++++++++++++++++ .../source/pinmux/pinmux_registers.hpp | 65 +++++++ .../libexosphere/source/uart/uart_api.cpp | 56 +++++- 21 files changed, 691 insertions(+), 17 deletions(-) create mode 100644 exosphere2/program/source/smc/secmon_smc_se_lock.cpp create mode 100644 exosphere2/program/source/smc/secmon_smc_se_lock.hpp create mode 100644 libraries/libexosphere/include/exosphere/charger.hpp create mode 100644 libraries/libexosphere/include/exosphere/pinmux.hpp create mode 100644 libraries/libexosphere/source/charger/charger_api.cpp create mode 100644 libraries/libexosphere/source/pinmux/pinmux_api.cpp create mode 100644 libraries/libexosphere/source/pinmux/pinmux_registers.hpp diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 773feb347..ea37f82d5 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -32,14 +32,14 @@ namespace ams::secmon { void Main() { /* Set library register addresses. */ - /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ + actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress()); - /* pinmux::SetRegisterAddress(); */ + pinmux::SetRegisterAddress(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress()); pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index a2c1d0088..229bd121b 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -28,6 +28,17 @@ namespace ams::secmon { InvalidateL3Entries(l3, boot_code, boot_code_size); } + constexpr void UnmapTzramImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Unmap the L3 entries corresponding to tzram. */ + InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize()); + + /* Unmap the L2 entries corresponding to those L3 entries. */ + InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize()); + + /* Unmap the L1 entry corresponding to to those L2 entries. */ + InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); + } + } void UnmapBootCode() { @@ -49,4 +60,16 @@ namespace ams::secmon { secmon::EnsureMappingConsistency(); } + void UnmapTzram() { + /* Get the tables. */ + u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + /* Unmap. */ + UnmapTzramImpl(l1, l2_l3, l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(); + } + } diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp index 9d0aac678..aa8c7f32e 100644 --- a/exosphere2/program/source/secmon_map.hpp +++ b/exosphere2/program/source/secmon_map.hpp @@ -18,6 +18,6 @@ namespace ams::secmon { - /* TODO */ + void UnmapTzram(); } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index fa791e079..f6fc4a84f 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -16,9 +16,12 @@ #include <exosphere.hpp> #include "secmon_setup.hpp" #include "secmon_error.hpp" +#include "secmon_map.hpp" #include "secmon_cpu_context.hpp" #include "secmon_interrupt_handler.hpp" #include "secmon_misc.hpp" +#include "smc/secmon_smc_power_management.hpp" +#include "smc/secmon_smc_se_lock.hpp" namespace ams::secmon { @@ -887,11 +890,58 @@ namespace ams::secmon { reg::Read (MC + MC_SMMU_TLB_CONFIG); } + void ValidateResetExpected() { + /* We're coming out of reset, so check that we expected to come out of reset. */ + if (!IsResetExpected()) { + secmon::SetError(pkg1::ErrorInfo_UnexpectedReset); + AMS_ABORT("unexpected reset"); + } + SetResetExpected(false); + } + void ActmonInterruptHandler() { SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt); AMS_ABORT("actmon observed bpmp wakeup"); } + void ExitChargerHiZMode() { + /* Setup I2c-1. */ + pinmux::SetupI2c1(); + clkrst::EnableI2c1Clock(); + + /* Initialize I2c-1. */ + i2c::Initialize(i2c::Port_1); + + /* Exit Hi-Z mode. */ + charger::ExitHiZMode(); + + /* Disable clock to I2c-1. */ + clkrst::DisableI2c1Clock(); + } + + bool IsExitLp0() { + return reg::Read(MC + MC_SECURITY_CFG3) == 0; + } + + void LogExitLp0() { + /* NOTE: Nintendo only does this on dev, but we will always do it. */ + if (true /* !pkg1::IsProduction() */) { + log::Initialize(); + log::SendText("OHAYO\n", 6); + log::Flush(); + } + } + + void SetupForLp0Exit() { + /* Exit HiZ mode in charger, if we need to. */ + if (smc::IsChargerHiZModeEnabled()) { + ExitChargerHiZMode(); + } + + /* Unlock the security engine. */ + secmon::smc::UnlockSecurityEngine(); + } + } void Setup1() { @@ -908,6 +958,14 @@ namespace ams::secmon { gic::InitializeCommon(); } + void Setup1ForWarmboot() { + /* Initialize the security engine. */ + se::Initialize(); + + /* Initialize the gic. */ + gic::InitializeCommon(); + } + void SaveSecurityEngineAesKeySlotTestVector() { GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector)); } @@ -1007,7 +1065,36 @@ namespace ams::secmon { } void SetupSocSecurityWarmboot() { - /* ... */ + /* Check that we're allowed to continue. */ + ValidateResetExpected(); + + /* Unmap the tzram identity mapping. */ + UnmapTzram(); + + /* If we're exiting LP0, there's a little more work for us to do. */ + if (IsExitLp0()) { + /* Log that we're exiting LP0. */ + LogExitLp0(); + + /* Perform initial setup. */ + Setup1ForWarmboot(); + + /* Setup the Soc security. */ + SetupSocSecurity(); + + /* Set the PMC and MC as secure-only. */ + SetupPmcAndMcSecure(); + + /* Perform Lp0-exit specific init. */ + SetupForLp0Exit(); + + /* Setup the Soc protections. */ + SetupSocProtections(); + } + + /* Perform remaining CPU initialization. */ + SetupCpuCoreContext(); + SetupCpuSErrorDebug(); } void SetupSocProtections() { diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 7434bf217..a6f664957 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -19,6 +19,12 @@ namespace ams::secmon::smc { + namespace { + + constinit bool g_charger_hi_z_mode_enabled = false; + + } + SmcResult SmcPowerOffCpu(const SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; @@ -34,4 +40,12 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + bool IsChargerHiZModeEnabled() { + return g_charger_hi_z_mode_enabled; + } + + void SetChargerHiZModeEnabled(bool en) { + g_charger_hi_z_mode_enabled = en; + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp index 7b67d6e78..bc6f45229 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.hpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp @@ -24,4 +24,7 @@ namespace ams::secmon::smc { SmcResult SmcSuspendCpu(const SmcArguments &args); + bool IsChargerHiZModeEnabled(); + void SetChargerHiZModeEnabled(bool en); + } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp new file mode 100644 index 000000000..d748ce70a --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp @@ -0,0 +1,41 @@ +/* + * 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_error.hpp" +#include "secmon_smc_se_lock.hpp" + +namespace ams::secmon::smc { + + namespace { + + constinit std::atomic_bool g_is_locked = false; + + } + + bool TryLockSecurityEngine() { + bool value = false; + return g_is_locked.compare_exchange_strong(value, true); + } + + void UnlockSecurityEngine() { + g_is_locked = false; + } + + bool IsSecurityEngineLocked() { + return g_is_locked; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp new file mode 100644 index 000000000..db8e62cf0 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp @@ -0,0 +1,26 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + bool TryLockSecurityEngine(); + void UnlockSecurityEngine(); + bool IsSecurityEngineLocked(); + +} diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index c6468ab62..e93e8b8fa 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -22,6 +22,7 @@ #include <exosphere/util.hpp> #include <exosphere/mmu.hpp> #include <exosphere/br.hpp> +#include <exosphere/charger.hpp> #include <exosphere/gic.hpp> #include <exosphere/wdt.hpp> #include <exosphere/pkg1.hpp> @@ -32,6 +33,7 @@ #include <exosphere/fuse.hpp> #include <exosphere/i2c.hpp> #include <exosphere/uart.hpp> +#include <exosphere/pinmux.hpp> #include <exosphere/pmic.hpp> #include <exosphere/log.hpp> #include <exosphere/clkrst.hpp> diff --git a/libraries/libexosphere/include/exosphere/charger.hpp b/libraries/libexosphere/include/exosphere/charger.hpp new file mode 100644 index 000000000..7241f3ab7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/charger.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::charger { + + bool IsHiZMode(); + void EnterHiZMode(); + void ExitHiZMode(); + +} diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp index 2f0143977..e4b31740d 100644 --- a/libraries/libexosphere/include/exosphere/clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -26,5 +26,8 @@ namespace ams::clkrst { void EnableUartBClock(); void EnableUartCClock(); void EnableActmonClock(); + void EnableI2c1Clock(); + + void DisableI2c1Clock(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp index 2f1cca8c5..3c54fbedc 100644 --- a/libraries/libexosphere/include/exosphere/log.hpp +++ b/libraries/libexosphere/include/exosphere/log.hpp @@ -19,5 +19,9 @@ namespace ams::log { void Initialize(); + void Finalize(); + + void SendText(const void *text, size_t size); + void Flush(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pinmux.hpp b/libraries/libexosphere/include/exosphere/pinmux.hpp new file mode 100644 index 000000000..f79035b27 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pinmux.hpp @@ -0,0 +1,29 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::pinmux { + + void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address); + + void SetupUartA(); + void SetupUartB(); + void SetupUartC(); + void SetupI2c1(); + void SetupI2c5(); + +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 035b28594..1529b566f 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -56,12 +56,16 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) /* CLK_SOURCE */ +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124) +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) #define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3e8) /* CLK_ENB_*_INDEX */ +#define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX (0x0F) #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) @@ -80,8 +84,11 @@ DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, O DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON); -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); + +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); diff --git a/libraries/libexosphere/include/exosphere/uart.hpp b/libraries/libexosphere/include/exosphere/uart.hpp index 920f39125..d3f921693 100644 --- a/libraries/libexosphere/include/exosphere/uart.hpp +++ b/libraries/libexosphere/include/exosphere/uart.hpp @@ -39,4 +39,8 @@ namespace ams::uart { void Initialize(Port port, int baud_rate, u32 flags); -} \ No newline at end of file + void SendText(Port port, const void *data, size_t size); + + void WaitFlush(Port port); + +} diff --git a/libraries/libexosphere/source/charger/charger_api.cpp b/libraries/libexosphere/source/charger/charger_api.cpp new file mode 100644 index 000000000..7aeb5c37a --- /dev/null +++ b/libraries/libexosphere/source/charger/charger_api.cpp @@ -0,0 +1,55 @@ +/* + * 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> + +namespace ams::charger { + + namespace { + + /* https://www.ti.com/lit/ds/symlink/bq24193.pdf */ + constexpr inline int I2cAddressBq24193 = 0x6B; + + constexpr inline int Bq24193RegisterInputSourceControl = 0x00; + + /* 8.5.1.1 EN_HIZ */ + enum EnHiZ : u8 { + EnHiZ_Disable = (0u << 7), + EnHiZ_Enable = (1u << 7), + + EnHiZ_Mask = (1u << 7), + }; + + } + + bool IsHiZMode() { + return (i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl) & EnHiZ_Mask) == EnHiZ_Enable; + } + + void EnterHiZMode() { + u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl); + ctrl &= ~EnHiZ_Mask; + ctrl |= EnHiZ_Enable; + i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl); + } + + void ExitHiZMode() { + u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl); + ctrl &= ~EnHiZ_Mask; + ctrl |= EnHiZ_Disable; + i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl); + } + +} diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp index 76601b6eb..c9ac4f983 100644 --- a/libraries/libexosphere/source/clkrst/clkrst_api.cpp +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -49,13 +49,13 @@ namespace ams::clkrst { reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 0)); } - // void DisableClock(const ClockParameters ¶m) { - // /* Hold reset. */ - // reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); - // - // /* Disable clock. */ - // reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); - // } + void DisableClock(const ClockParameters ¶m) { + /* Hold reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Disable clock. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + } #define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \ constexpr inline const ClockParameters _VARNAME_ = { \ @@ -70,6 +70,8 @@ namespace ams::clkrst { DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(I2c1Clock, L, I2C1, CLK_M, 0); + DEFINE_CLOCK_PARAMETERS(I2c5Clock, H, I2C5, CLK_M, 0); DEFINE_CLOCK_PARAMETERS(ActmonClock, V, ACTMON, CLK_M, 0); } @@ -98,4 +100,16 @@ namespace ams::clkrst { EnableClock(ActmonClock); } + void EnableI2c1Clock() { + EnableClock(I2c1Clock); + } + + void EnableI2c5Clock() { + EnableClock(I2c1Clock); + } + + void DisableI2c1Clock() { + DisableClock(I2c1Clock); + } + } diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp index 0cf20e207..2ea3c5c70 100644 --- a/libraries/libexosphere/source/log/log_api.cpp +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -40,9 +40,30 @@ namespace ams::log { } }(); + ALWAYS_INLINE void SetupUart() { + if constexpr (UartLogPort == uart::Port_ReservedDebug) { + /* Logging to the debug port. */ + pinmux::SetupUartA(); + clkrst::EnableUartAClock(); + } else if constexpr (UartLogPort == uart::Port_LeftJoyCon) { + /* Logging to left joy-con (e.g. with Joyless). */ + pinmux::SetupUartB(); + clkrst::EnableUartBClock(); + } else if constexpr (UartLogPort == uart::Port_RightJoyCon) { + /* Logging to right joy-con (e.g. with Joyless). */ + pinmux::SetupUartC(); + clkrst::EnableUartCClock(); + } else { + __builtin_unreachable(); + } + } + } void Initialize() { + /* Initialize pinmux and clock for the target uart port. */ + SetupUart(); + /* Initialize the target uart port. */ uart::Initialize(UartLogPort, 115200, UartPortFlags); @@ -50,4 +71,20 @@ namespace ams::log { g_initialized_uart = true; } + void Finalize() { + g_initialized_uart = false; + } + + void SendText(const void *text, size_t size) { + if (g_initialized_uart) { + uart::SendText(UartLogPort, text, size); + } + } + + void Flush() { + if (g_initialized_uart) { + uart::WaitFlush(UartLogPort); + } + } + } \ No newline at end of file diff --git a/libraries/libexosphere/source/pinmux/pinmux_api.cpp b/libraries/libexosphere/source/pinmux/pinmux_api.cpp new file mode 100644 index 000000000..513797dfb --- /dev/null +++ b/libraries/libexosphere/source/pinmux/pinmux_api.cpp @@ -0,0 +1,181 @@ +/* + * 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 "pinmux_registers.hpp" + +namespace ams::pinmux { + + namespace { + + constinit uintptr_t g_pinmux_address = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constinit uintptr_t g_gpio_address = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + + } + + void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address) { + g_pinmux_address = pinmux_address; + g_gpio_address = gpio_address; + } + + void SetupUartA() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-A. */ + reg::Write(PINMUX + PINMUX_AUX_UART1_TX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_RX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_RTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_CTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + + void SetupUartB() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-B. */ + reg::Write(PINMUX + PINMUX_AUX_UART2_TX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_RX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_RTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_CTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + /* Configure GPIO for Uart-B. */ + reg::ReadWrite(g_gpio_address + 0x108, REG_BITS_VALUE(0, 4, 0)); + } + + void SetupUartC() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-B. */ + reg::Write(PINMUX + PINMUX_AUX_UART3_TX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_RX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_RTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_CTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + /* Configure GPIO for Uart-C. */ + reg::ReadWrite(g_gpio_address + 0x00C, REG_BITS_VALUE(1, 4, 0)); + } + + void SetupI2c1() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure I2c1 */ + reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + + void SetupI2c5() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure I2c5 */ + reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pinmux/pinmux_registers.hpp b/libraries/libexosphere/source/pinmux/pinmux_registers.hpp new file mode 100644 index 000000000..4fd1c4d51 --- /dev/null +++ b/libraries/libexosphere/source/pinmux/pinmux_registers.hpp @@ -0,0 +1,65 @@ +/* + * 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> + +namespace ams::pinmux { + + #define PINMUX_AUX_GEN1_I2C_SCL (0x30BC) + #define PINMUX_AUX_GEN1_I2C_SDA (0x30C0) + #define PINMUX_AUX_PWR_I2C_SCL (0x30DC) + #define PINMUX_AUX_PWR_I2C_SDA (0x30E0) + + #define PINMUX_AUX_UART1_TX (0x30E4) + #define PINMUX_AUX_UART1_RX (0x30E8) + #define PINMUX_AUX_UART1_RTS (0x30EC) + #define PINMUX_AUX_UART1_CTS (0x30F0) + #define PINMUX_AUX_UART2_TX (0x30F4) + #define PINMUX_AUX_UART2_RX (0x30F8) + #define PINMUX_AUX_UART2_RTS (0x30FC) + #define PINMUX_AUX_UART2_CTS (0x3100) + #define PINMUX_AUX_UART3_TX (0x3104) + #define PINMUX_AUX_UART3_RX (0x3108) + #define PINMUX_AUX_UART3_RTS (0x310C) + #define PINMUX_AUX_UART3_CTS (0x3110) + + #define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME) + #define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE) + #define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM) + #define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE); + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3); + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3); + +} diff --git a/libraries/libexosphere/source/uart/uart_api.cpp b/libraries/libexosphere/source/uart/uart_api.cpp index 533748106..02e6d59a3 100644 --- a/libraries/libexosphere/source/uart/uart_api.cpp +++ b/libraries/libexosphere/source/uart/uart_api.cpp @@ -58,6 +58,28 @@ namespace ams::uart { } } + constexpr inline u32 LockBit = (1 << 6); + + void Lock(volatile UartRegisters *reg) { + while (true) { + if (reg->mie != 0) { + continue; + } + + reg->irda_csr = LockBit; + + if (reg->mie == 0) { + break; + } + + reg->irda_csr = 0; + } + } + + void Unlock(volatile UartRegisters *reg) { + reg->irda_csr = 0; + } + } void SetRegisterAddress(uintptr_t address) { @@ -110,4 +132,36 @@ namespace ams::uart { uart->spr = 0; } -} \ No newline at end of file + void SendText(Port port, const void *data, size_t size) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Get pointer to data. */ + const u8 *p = static_cast<const u8 *>(data); + + /* Lock the uart registers. */ + Lock(uart); + ON_SCOPE_EXIT { Unlock(uart); }; + + /* Send each byte. */ + for (size_t i = 0; i < size; ++i) { + WaitFifoNotFull(uart); + + if (p[i] == '\n') { + *reinterpret_cast<volatile u8 *>(std::addressof(uart->thr)) = '\r'; + WaitFifoNotFull(uart); + } + + *reinterpret_cast<volatile u8 *>(std::addressof(uart->thr)) = p[i]; + } + } + + void WaitFlush(Port port) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Wait for idle. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE); + } + +} From b7ff9e8fccfb496ed8ace1a5ef0fe56787c82899 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 18:10:54 -0700 Subject: [PATCH 051/118] exo2: reorganize to save >0x300 bytes and 0x100 of stack in main --- exosphere2/program/program.ld | 31 ++++--- .../source/boot/secmon_boot_functions.cpp | 15 ++-- .../source/boot/secmon_boot_key_data.cpp | 82 ------------------- .../source/boot/secmon_boot_key_data.hpp | 26 ------ .../source/boot/secmon_boot_key_data.s | 77 +++++++++++++++++ .../program/source/boot/secmon_main.cpp | 5 +- .../program/source/boot/secmon_package2.cpp | 1 - .../program/source/secmon_start_virtual.s | 4 + .../exosphere/secmon/secmon_memory_layout.hpp | 2 +- .../secmon/secmon_volatile_context.hpp | 38 ++++++++- 10 files changed, 146 insertions(+), 135 deletions(-) delete mode 100644 exosphere2/program/source/boot/secmon_boot_key_data.cpp delete mode 100644 exosphere2/program/source/boot/secmon_boot_key_data.hpp create mode 100644 exosphere2/program/source/boot/secmon_boot_key_data.s diff --git a/exosphere2/program/program.ld b/exosphere2/program/program.ld index b451dd5c7..8ca399862 100644 --- a/exosphere2/program/program.ld +++ b/exosphere2/program/program.ld @@ -12,10 +12,10 @@ MEMORY /* However, we can't know for sure how big warmboot is, so we'll just say it's 2K. */ warmboot_text : ORIGIN = ORIGIN(tzram) + 10K, LENGTH = 2K - main : ORIGIN = 0x1F00C0000, LENGTH = 48K - tzram_boot_code : ORIGIN = 0x1F01C0800, LENGTH = 6K + main : ORIGIN = 0x1F00C0000, LENGTH = 48K + tzram_boot : ORIGIN = 0x1F01C0000, LENGTH = 8K - glob : ORIGIN = 0x040032000, LENGTH = 64K + glob : ORIGIN = 0x040032000, LENGTH = 64K } SECTIONS @@ -88,6 +88,17 @@ SECTIONS __bootcode_end__ = ABSOLUTE(.); __program_start__ = ABSOLUTE(.); + + .tzram_boot_volatile_data : { + KEEP (*(.volatile_keys .volatile_keys.*)) + } >tzram_boot AT>glob + + .tzram_boot_volatile_data.fill : { + FILL(0x00000000); + . = ORIGIN(tzram_boot) + 0x7FF; + BYTE(0x00); + } >tzram_boot AT>glob + .tzram_boot_code : { KEEP(secmon_main.o(.text*)) @@ -96,23 +107,20 @@ SECTIONS KEEP(secmon_boot_config.o(.text*)) KEEP(secmon_boot_setup.o(.text*)) KEEP(secmon_package2.o(.text*)) - KEEP(secmon_key_data.o(.text*)) secmon_main.o(.rodata*) secmon_boot_functions.o(.rodata*) secmon_boot_cache.o(.rodata*) secmon_boot_config.o(.rodata*) secmon_boot_setup.o(.rodata*) secmon_package2.o(.rodata*) - secmon_key_data.o(.rodata*) secmon_main.o(.data*) secmon_boot_functions.o(.data*) secmon_boot_cache.o(.data*) secmon_boot_config.o(.data*) secmon_boot_setup.o(.data*) secmon_package2.o(.data*) - secmon_key_data.o(.data*) . = ALIGN(8); - } >tzram_boot_code AT>glob + } >tzram_boot AT>glob .tzram_boot_code.bss : { @@ -123,16 +131,15 @@ SECTIONS secmon_boot_config.o(.bss* COMMON) secmon_boot_setup.o(.bss* COMMON) secmon_package2.o(.bss* COMMON) - secmon_key_data.o(.bss* COMMON) __boot_bss_end__ = ABSOLUTE(.); - } >tzram_boot_code AT>glob + } >tzram_boot AT>glob .tzram_boot_code.fill : { FILL(0x00000000); - . = ORIGIN(tzram_boot_code) + LENGTH(tzram_boot_code) - 1; + . = ORIGIN(tzram_boot) + LENGTH(tzram_boot) - 1; BYTE(0x00); - } > tzram_boot_code AT>glob + } > tzram_boot AT>glob .vectors : { @@ -155,7 +162,7 @@ SECTIONS KEEP(tsec_*.o(.data*)) } >warmboot_text AT>glob - .text : + .text ORIGIN(main) + SIZEOF(.vectors) + SIZEOF(.warmboot) : { *(.text.unlikely .text.*_unlikely .text.unlikely.*) *(.text.exit .text.exit.*) diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 6c33be920..8773f4f4e 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -18,7 +18,6 @@ #include "secmon_boot.hpp" #include "secmon_boot_cache.hpp" #include "secmon_boot_functions.hpp" -#include "secmon_boot_key_data.hpp" namespace ams::secmon::boot { @@ -27,7 +26,7 @@ namespace ams::secmon::boot { constexpr inline uintptr_t SYSCTR0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); NOINLINE void DecryptPayload(uintptr_t dst, uintptr_t src, size_t size, const void *iv, size_t iv_size, u8 key_generation) { - secmon::boot::DecryptPackage2(reinterpret_cast<void *>(dst), size, reinterpret_cast<void *>(src), size, Package2AesKey, util::size(Package2AesKey), iv, iv_size, key_generation); + secmon::boot::DecryptPackage2(reinterpret_cast<void *>(dst), size, reinterpret_cast<void *>(src), size, secmon::boot::GetPackage2AesKey(), crypto::AesEncryptor128::KeySize, iv, iv_size, key_generation); } } @@ -74,7 +73,7 @@ namespace ams::secmon::boot { /* Determine if the bc is valid for the device. */ bool valid_for_device = false; { - const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, BootConfigRsaPublicModulus, util::size(BootConfigRsaPublicModulus)); + const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, secmon::boot::GetBootConfigRsaModulus(), se::RsaSize); if (valid_signature) { valid_for_device = secmon::boot::VerifyBootConfigEcid(*bc); } @@ -138,10 +137,10 @@ namespace ams::secmon::boot { } void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify) { - if (pkg1::IsProductionForPublicKey()) { - CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, Package2RsaPublicModulusProduction, util::size(Package2RsaPublicModulusProduction)), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed"); - } else if (verify) { - CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, Package2RsaPublicModulusDevelopment, util::size(Package2RsaPublicModulusDevelopment)), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed"); + const u8 * const mod = secmon::boot::GetPackage2RsaModulus(pkg1::IsProductionForPublicKey()); + const size_t mod_size = se::RsaSize; + if (verify) { + CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, mod, mod_size), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed"); } } @@ -150,7 +149,7 @@ namespace ams::secmon::boot { constexpr int IvSize = 0x10; /* Decrypt the header. */ - DecryptPackage2(dst, sizeof(*dst), std::addressof(src), sizeof(src), Package2AesKey, util::size(Package2AesKey), std::addressof(src), IvSize, src.GetKeyGeneration()); + DecryptPackage2(dst, sizeof(*dst), std::addressof(src), sizeof(src), secmon::boot::GetPackage2AesKey(), crypto::AesEncryptor128::KeySize, std::addressof(src), IvSize, src.GetKeyGeneration()); /* Copy back the iv, which encodes encrypted metadata. */ std::memcpy(dst, std::addressof(src), IvSize); diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.cpp b/exosphere2/program/source/boot/secmon_boot_key_data.cpp deleted file mode 100644 index e3e74dd6e..000000000 --- a/exosphere2/program/source/boot/secmon_boot_key_data.cpp +++ /dev/null @@ -1,82 +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 <exosphere.hpp> -#include "secmon_boot_key_data.hpp" - -namespace ams::secmon::boot { - - constinit const u8 BootConfigRsaPublicModulus[se::RsaSize] = { - 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, - 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, - 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, - 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, - 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, - 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, - 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, - 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, - 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, - 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, - 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, - 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, - 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, - 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, - 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, - 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D - }; - - constinit const u8 Package2RsaPublicModulusProduction[se::RsaSize] = { - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - }; - - constinit const u8 Package2RsaPublicModulusDevelopment[se::RsaSize] = { - 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, - 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, - 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, - 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, - 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, - 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, - 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, - 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, - 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, - 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, - 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, - 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, - 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, - 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, - 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, - 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B - }; - - constinit const u8 Package2AesKey[] { - 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 - }; - -} diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.hpp b/exosphere2/program/source/boot/secmon_boot_key_data.hpp deleted file mode 100644 index 06513a5d3..000000000 --- a/exosphere2/program/source/boot/secmon_boot_key_data.hpp +++ /dev/null @@ -1,26 +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/>. - */ -#pragma once -#include <exosphere.hpp> - -namespace ams::secmon::boot { - - extern const u8 BootConfigRsaPublicModulus[se::RsaSize]; - extern const u8 Package2RsaPublicModulusDevelopment[se::RsaSize]; - extern const u8 Package2RsaPublicModulusProduction[se::RsaSize]; - extern const u8 Package2AesKey[se::AesBlockSize]; - -} diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.s b/exosphere2/program/source/boot/secmon_boot_key_data.s new file mode 100644 index 000000000..2990ea31a --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_key_data.s @@ -0,0 +1,77 @@ +/* + * 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/>. + */ + + + +.section .volatile_keys._ZN3ams6secmon4boot15VolatileKeyDataE, "aw", %progbits +.global _ZN3ams6secmon4boot15VolatileKeyDataE +_ZN3ams6secmon4boot15VolatileKeyDataE: +/* BootConfig Rsa Modulus. */ +.byte 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64 +.byte 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33 +.byte 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB +.byte 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9 +.byte 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B +.byte 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25 +.byte 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49 +.byte 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24 +.byte 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF +.byte 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E +.byte 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6 +.byte 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9 +.byte 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12 +.byte 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5 +.byte 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93 +.byte 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D + +/* Package2 Development Rsa Modulus. */ +.byte 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99 +.byte 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7 +.byte 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6 +.byte 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38 +.byte 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83 +.byte 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0 +.byte 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6 +.byte 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50 +.byte 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7 +.byte 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42 +.byte 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E +.byte 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8 +.byte 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7 +.byte 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4 +.byte 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46 +.byte 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B + +/* Package2 Production Rsa Modulus. */ +.byte 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59 +.byte 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE +.byte 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5 +.byte 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74 +.byte 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48 +.byte 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE +.byte 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32 +.byte 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A +.byte 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B +.byte 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3 +.byte 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9 +.byte 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47 +.byte 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D +.byte 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2 +.byte 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF +.byte 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F + +/* Package2 Aes Key Source. */ +.byte 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index ea37f82d5..aa603eec9 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -73,9 +73,6 @@ namespace ams::secmon { /* Alert the bootloader that we're initialized. */ secmon_params.secmon_state = pkg1::SecureMonitorState_Initialized; - - /* Initialize the random cache. */ - secmon::smc::FillRandomCache(); } /* Wait for NX Bootloader to finish loading the BootConfig. */ @@ -129,7 +126,7 @@ namespace ams::secmon { secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedPackage2); /* Parse and decrypt the package2 header. */ - pkg2::Package2Meta pkg2_meta; + pkg2::Package2Meta &pkg2_meta = secmon::boot::GetEphemeralPackage2Meta(); const uintptr_t pkg2_payloads_start = MemoryRegionDramPackage2.GetAddress() + sizeof(pkg2::Package2Header); { /* Read the encrypred header. */ diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp index 3a440b373..32e8e8a7c 100644 --- a/exosphere2/program/source/boot/secmon_package2.cpp +++ b/exosphere2/program/source/boot/secmon_package2.cpp @@ -17,7 +17,6 @@ #include "../secmon_error.hpp" #include "../secmon_key_storage.hpp" #include "secmon_boot.hpp" -#include "secmon_boot_key_data.hpp" namespace ams::secmon::boot { diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 8d918fbf3..1356f94ab 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -38,6 +38,10 @@ _ZN3ams6secmon5StartEv: ldr x20, =0x1F01F9000 mov sp, x20 + /* Initialize the random cache. */ + /* NOTE: Nintendo does this much earlier, but we reuse volatile space. */ + bl _ZN3ams6secmon3smc15FillRandomCacheEv + /* Unmap the boot code region. */ bl _ZN3ams6secmon13UnmapBootCodeEv diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index ffe9625e7..ce0fa4cf5 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -280,7 +280,7 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramL2L3PageTable)); static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramL2L3PageTable)); - constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(UINT64_C(0x7C010800), 0xD800); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(UINT64_C(0x7C010000), 0xE000); constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(UINT64_C(0x40032000), 0xC000); constexpr inline const MemoryRegion MemoryRegionPhysicalIramWarmbootBin = MemoryRegion(UINT64_C(0x4003E000), 0x17F0); diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp index c8bc4e37b..332108181 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp @@ -15,6 +15,7 @@ */ #pragma once #include <vapours.hpp> +#include <exosphere/pkg2.hpp> namespace ams::secmon { @@ -23,10 +24,22 @@ namespace ams::secmon { constexpr inline const size_t CoreExceptionStackSize = 0x80; + /* Volatile keydata that we lose access to after boot. */ + struct VolatileKeys { + u8 boot_config_rsa_modulus[0x100]; + u8 package2_dev_rsa_modulus[0x100]; + u8 package2_prod_rsa_modulus[0x100]; + u8 package2_aes_key[0x10]; + }; + /* Nintendo uses the bottom 0x740 of this as a stack for warmboot setup, and another 0x740 for the core 0/1/2 SMC stacks. */ /* This is...wasteful. The warmboot stack is not deep. We will thus save 1K+ of nonvolatile storage by keeping the random cache in here. */ struct VolatileData { - u8 random_cache[0x400]; + union { + u8 random_cache[0x400]; + VolatileKeys keys; + pkg2::Package2Meta pkg2_meta; + }; u8 se_work_block[crypto::AesEncryptor128::BlockSize]; u8 reserved_danger_zone[0x30]; /* This memory is "available", but careful consideration must be taken before declaring it used. */ u8 warmboot_stack[0x380]; @@ -35,6 +48,8 @@ namespace ams::secmon { }; static_assert(util::is_pod<VolatileData>::value); static_assert(sizeof(VolatileData) == 0x1000); + static_assert(sizeof(VolatileKeys{}.boot_config_rsa_modulus) == sizeof(pkg2::Package2Meta)); + static_assert(offsetof(VolatileData, keys.boot_config_rsa_modulus) == offsetof(VolatileData, pkg2_meta)); ALWAYS_INLINE VolatileData &GetVolatileData() { return *MemoryRegionVirtualTzramVolatileData.GetPointer<VolatileData>(); @@ -52,6 +67,27 @@ namespace ams::secmon { return GetVolatileData().se_work_block; } + namespace boot { + + ALWAYS_INLINE const u8 *GetBootConfigRsaModulus() { + return GetVolatileData().keys.boot_config_rsa_modulus; + } + + ALWAYS_INLINE const u8 *GetPackage2RsaModulus(bool is_prod) { + auto &volatile_data = GetVolatileData(); + return is_prod ? volatile_data.keys.package2_prod_rsa_modulus : volatile_data.keys.package2_dev_rsa_modulus; + } + + ALWAYS_INLINE const u8 *GetPackage2AesKey() { + return GetVolatileData().keys.package2_aes_key; + } + + ALWAYS_INLINE pkg2::Package2Meta &GetEphemeralPackage2Meta() { + return GetVolatileData().pkg2_meta; + } + + } + constexpr inline const Address WarmbootStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, warmboot_stack) + sizeof(VolatileData::warmboot_stack); constexpr inline const Address Core012SmcStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, core012_smc_stack) + sizeof(VolatileData::core012_smc_stack); From 63629b22a1299a06392460d1624641153fa29109 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 20:44:13 -0700 Subject: [PATCH 052/118] exo2: use N's strategy for randcache, it's better --- .../program/source/smc/secmon_random_cache.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere2/program/source/smc/secmon_random_cache.cpp index bcbed1df7..b5cdf1468 100644 --- a/exosphere2/program/source/smc/secmon_random_cache.cpp +++ b/exosphere2/program/source/smc/secmon_random_cache.cpp @@ -86,17 +86,15 @@ namespace ams::secmon::smc { u8 * const cache = GetRandomBytesCache(); u8 * cur_dst = static_cast<u8 *>(dst); - /* NOTE: Nintendo does not do bounds checking here, and does not do multiple reads when the get would wrap around. */ - while (size > 0) { - const size_t copy_size = std::min(size, static_cast<size_t>(GetRandomBytesCacheSize() - g_random_offset_low)); - std::memcpy(cur_dst, cache + g_random_offset_low, copy_size); + /* Copy out the requested size. */ + std::memcpy(dst, cache + g_random_offset_low, size); - cur_dst += copy_size; - size -= copy_size; + /* Advance. */ + g_random_offset_low += size; - if (g_random_offset_low + copy_size >= GetRandomBytesCacheSize()) { - g_random_offset_low = 0; - } + /* Ensure that at all times g_random_offset_low is not within 0x38 bytes of the end of the pool. */ + if (g_random_offset_low + MaxRandomBytes >= GetRandomBytesCacheSize()) { + g_random_offset_low = 0; } } From 435d2fb0c3ff277d299685e60699ddc1b81c6b5e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 20:44:59 -0700 Subject: [PATCH 053/118] exo2: remove unused lvars --- exosphere2/program/source/smc/secmon_random_cache.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere2/program/source/smc/secmon_random_cache.cpp index b5cdf1468..13b1fd941 100644 --- a/exosphere2/program/source/smc/secmon_random_cache.cpp +++ b/exosphere2/program/source/smc/secmon_random_cache.cpp @@ -83,11 +83,8 @@ namespace ams::secmon::smc { } void GetRandomFromCache(void *dst, size_t size) { - u8 * const cache = GetRandomBytesCache(); - u8 * cur_dst = static_cast<u8 *>(dst); - /* Copy out the requested size. */ - std::memcpy(dst, cache + g_random_offset_low, size); + std::memcpy(dst, GetRandomBytesCache() + g_random_offset_low, size); /* Advance. */ g_random_offset_low += size; From ab703646d58eb7c10b4d46e9f591d53bd0c11bad Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 20:51:58 -0700 Subject: [PATCH 054/118] exo2: by default, map end of iram for debug --- exosphere2/program/source/boot/secmon_make_page_table.cpp | 5 +++++ .../include/exosphere/secmon/secmon_memory_layout.hpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp index 876bc54c0..c30ebee86 100644 --- a/exosphere2/program/source/boot/secmon_make_page_table.cpp +++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp @@ -100,6 +100,11 @@ namespace ams::secmon::boot { /* Map the IRAM SC7 firmware region. */ SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Firmware.GetAddress(), MemoryRegionPhysicalIramSc7Firmware.GetAddress(), MemoryRegionVirtualIramSc7Firmware.GetSize(), MappingAttributesEl3NonSecureDevice); + /* Map the Debug region. */ + /* NOTE: This region is reserved for debug. By default it will be the last 0x8000 bytes of IRAM, but this is subject to change. */ + /* If you are doing development work for exosphere, feel free to locally change this to whatever is useful. */ + SetL3BlockEntry(l3, MemoryRegionVirtualDebug.GetAddress(), MemoryRegionPhysicalIram.GetEndAddress() - 0x8000, 0x8000, MappingAttributesEl3SecureDevice); + /* Map the TZRAM ro alias region. */ SetL3BlockEntry(l3, MemoryRegionVirtualTzramReadOnlyAlias.GetAddress(), MemoryRegionPhysicalTzramReadOnlyAlias.GetAddress(), MemoryRegionVirtualTzramReadOnlyAlias.GetSize(), MappingAttributesEl3SecureRoData); diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index ce0fa4cf5..a7818c11c 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -218,7 +218,7 @@ namespace ams::secmon { static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Firmware)); constexpr inline const MemoryRegion MemoryRegionVirtualDebug = MemoryRegion(UINT64_C(0x1F0160000), 0x10000); - static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDebug)); constexpr inline const MemoryRegion MemoryRegionVirtualTzramBootCode = MemoryRegion(UINT64_C(0x1F01C0000), 0x2000); constexpr inline const MemoryRegion MemoryRegionPhysicalTzramBootCode = MemoryRegion( UINT64_C(0x7C010000), 0x2000); From 3d6baf96a33ea47bc6b8884143543111ed3dbd6b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 21:48:07 -0700 Subject: [PATCH 055/118] exo2: implement SmcPowerCpuOn --- .../smc/secmon_smc_power_management.cpp | 97 ++++++++++++++++++- .../libexosphere/include/exosphere/reg.hpp | 20 ++-- .../include/exosphere/tegra/tegra_clkrst.hpp | 4 + .../include/exosphere/tegra/tegra_pmc.hpp | 35 +++++++ 4 files changed, 147 insertions(+), 9 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index a6f664957..5823ba71d 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" #include "secmon_smc_power_management.hpp" @@ -21,8 +22,78 @@ namespace ams::secmon::smc { namespace { + constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constinit bool g_charger_hi_z_mode_enabled = false; + constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = { + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3), + }; + + constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = { + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3, + }; + + bool IsCpuPoweredOn(const reg::BitsMask mask) { + return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON)); + } + + void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) { + /* If the cpu is already on, we have nothing to do. */ + if (IsCpuPoweredOn(mask)) { + return; + } + + /* Wait until nothing is being powergated. */ + int timeout = 5000; + while (true) { + if (reg::HasValue(APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + + /* Toggle on the cpu partition. */ + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid)); + + /* Wait up to 5000 us for the powergate to complete. */ + timeout = 5000; + while (true) { + if (IsCpuPoweredOn(mask)) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + } + + void ResetCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + + void StartCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + } SmcResult SmcPowerOffCpu(const SmcArguments &args) { @@ -31,8 +102,30 @@ namespace ams::secmon::smc { } SmcResult SmcPowerOnCpu(const SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Get and validate the core to power on. */ + const int which_core = args.r[1]; + if (!(0 <= which_core && which_core < NumCores)) { + return SmcResult::PsciInvalidParameters; + } + + /* Ensure the core isn't already on. */ + if (IsCoreOn(which_core)) { + return SmcResult::PsciAlreadyOn; + } + + /* Save the entry context. */ + SetEntryContext(which_core, args.r[2], args.r[3]); + + /* Reset the cpu. */ + ResetCpu(which_core); + + /* Turn on the core. */ + PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]); + + /* Start the core. */ + StartCpu(which_core); + + return SmcResult::PsciSuccess; } SmcResult SmcSuspendCpu(const SmcArguments &args) { diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp index a16d66193..aa0de0831 100644 --- a/libraries/libexosphere/include/exosphere/reg.hpp +++ b/libraries/libexosphere/include/exosphere/reg.hpp @@ -18,14 +18,18 @@ namespace ams::reg { - using BitsValue = std::tuple<u32, u32, u32>; - using BitsMask = std::tuple<u32, u32>; + using BitsValue = std::tuple<u16, u16, u32>; + using BitsMask = std::tuple<u16, u16>; - constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); } - constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); } - constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); } - constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); } - constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return static_cast<u32>(std::get<0>(v)); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return static_cast<u32>(std::get<0>(v)); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return static_cast<u32>(std::get<1>(v)); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return static_cast<u32>(std::get<1>(v)); } + constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return static_cast<u32>(std::get<2>(v)); } + + constexpr ALWAYS_INLINE ::ams::reg::BitsValue GetValue(const BitsMask m, const u32 v) { + return ::ams::reg::BitsValue{GetOffset(m), GetWidth(m), v}; + } constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) { return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); @@ -138,6 +142,8 @@ namespace ams::reg { #define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH} #define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE} + #define REG_BITS_VALUE_FROM_MASK(MASK, VALUE) ::ams::reg::GetValue(MASK, VALUE) + #define REG_NAMED_BITS_MASK(PREFIX, NAME) REG_BITS_MASK(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH) #define REG_NAMED_BITS_VALUE(PREFIX, NAME, VALUE) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, VALUE) #define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 1529b566f..3659194aa 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -71,6 +71,10 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) #define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_INDEX (0x17) +/* RST_CPUG_CMPLX_* */ +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET (0x450) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR (0x454) + DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, 19, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSEC_CLK_OVR_ON, 20, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index ee10391fa..27a809567 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -103,6 +103,41 @@ DEFINE_PMC_REG_BIT_ENUM(DPD_SAMPLE_ON, 0, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_ON, 0, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_TOGGLE_START, 8, DISABLE, ENABLE); + +DEFINE_PMC_REG(PWRGATE_TOGGLE_PARTID, 0, 5); + +enum APBDEV_PMC_PWRGATE_TOGGLE_PARTID : u8 { + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL = 0, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE = 2, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_PCX = 3, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_MPE = 6, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SAX = 8, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1 = 9, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2 = 10, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3 = 11, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0 = 14, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC = 15, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SOR = 17, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DIS = 18, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DISB = 19, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBA = 20, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBB = 21, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBC = 22, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VIC = 23, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_IRAM = 24, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVDEC = 25, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVJPG = 26, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_AUD = 27, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DFD = 28, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE2 = 29, +}; + +enum APBDEV_PMC_PWRGATE_STATUS_STATUS { + APBDEV_PMC_PWRGATE_STATUS_STATUS_OFF = 0, + APBDEV_PMC_PWRGATE_STATUS_STATUS_ON = 1, +}; + DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CRAIL, 0, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON); From 1fdd83628e6ab1d1fcba137aa2bce5012f6b35d5 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 13 May 2020 22:07:40 -0700 Subject: [PATCH 056/118] exo2: fix typo bug --- exosphere2/program/source/smc/secmon_smc_power_management.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 5823ba71d..2bfdaae52 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -54,7 +54,7 @@ namespace ams::secmon::smc { /* Wait until nothing is being powergated. */ int timeout = 5000; while (true) { - if (reg::HasValue(APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { + if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { break; } From 27843314a4e0b875dc8e69ed05a7b852c9e8b74f Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 14 May 2020 03:49:48 -0700 Subject: [PATCH 057/118] exo2: minor stack/mmu fixes, now gets to main on hw --- .../loader_stub/source/secmon_loader_main.cpp | 2 ++ exosphere2/loader_stub/source/start.s | 2 +- .../program/source/boot/secmon_main.cpp | 26 +++++++++---------- .../exosphere/mmu/mmu_api.arch.arm64.hpp | 6 ++--- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere2/loader_stub/source/secmon_loader_main.cpp index 70dc8555a..26b194413 100644 --- a/exosphere2/loader_stub/source/secmon_loader_main.cpp +++ b/exosphere2/loader_stub/source/secmon_loader_main.cpp @@ -24,6 +24,7 @@ namespace ams::secmon::loader { /* 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); @@ -31,6 +32,7 @@ namespace ams::secmon::loader { /* 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())(); diff --git a/exosphere2/loader_stub/source/start.s b/exosphere2/loader_stub/source/start.s index 2f432aaa7..7476605a7 100644 --- a/exosphere2/loader_stub/source/start.s +++ b/exosphere2/loader_stub/source/start.s @@ -95,7 +95,7 @@ _start: ERRATUM_INVALIDATE_BTB_AT_BOOT /* Set the stack pointer to a temporary location. */ - ldr x20, =0x7C010800 + ldr x20, =0x7C020000 mov sp, x20 /* Call our init array functions. */ diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index aa603eec9..6769cd9e7 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -32,19 +32,19 @@ namespace ams::secmon { void Main() { /* Set library register addresses. */ - actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); - clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); - flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); - fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); - gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); - i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); - i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress()); - pinmux::SetRegisterAddress(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress()); - pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); - se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); - uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); - wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); - util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); + clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); + flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); + fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); + gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); + i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); + i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress()); + pinmux::SetRegisterAddress(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress()); + pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); + se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); + uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); + wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); /* Get the secure monitor parameters. */ auto &secmon_params = *reinterpret_cast<pkg1::SecureMonitorParameters *>(MemoryRegionVirtualDeviceBootloaderParams.GetAddress()); diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp index d18e2b4f1..ada818af0 100644 --- a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp @@ -174,15 +174,15 @@ namespace ams::mmu::arch::arm64 { } constexpr u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) { - return address | static_cast<u64>(attr) | 0x1ul; + return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x1ul; } constexpr u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) { - return address | static_cast<u64>(attr) | 0x1ul; + return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x1ul; } constexpr u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) { - return address | static_cast<u64>(attr) | 0x3ul; + return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x3ul; } constexpr uintptr_t GetL2Offset(uintptr_t address) { From 8c4c1db506fea1cbf6e880f0d4ee4a7ce54b5746 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 14 May 2020 13:06:15 -0700 Subject: [PATCH 058/118] exo2: minor fixes, now completes main and receives SMCs on hw --- .../source/boot/secmon_make_page_table.cpp | 2 +- exosphere2/program/source/secmon_error.cpp | 56 +++++++++++++++++++ exosphere2/program/source/secmon_map.cpp | 27 ++++++--- exosphere2/program/source/secmon_setup.cpp | 8 +-- .../program/source/secmon_start_virtual.s | 12 ++-- .../program/source/smc/secmon_smc_handler.cpp | 15 +++++ .../exosphere/mmu/mmu_api.arch.arm64.hpp | 37 +++++++----- ...ecmon_configuration_context.arch.arm64.hpp | 10 ++-- .../libexosphere/source/se/se_execute.cpp | 4 +- .../libexosphere/source/se/se_management.cpp | 6 -- .../libexosphere/source/se/se_suspend.cpp | 4 +- 11 files changed, 134 insertions(+), 47 deletions(-) diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp index c30ebee86..4309cf994 100644 --- a/exosphere2/program/source/boot/secmon_make_page_table.cpp +++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp @@ -125,7 +125,7 @@ namespace ams::secmon::boot { SetL3BlockEntry(l3, MemoryRegionVirtualTzramConfigurationData.GetAddress(), MemoryRegionPhysicalTzramConfigurationData.GetAddress(), MemoryRegionVirtualTzramConfigurationData.GetSize(), MappingAttributesEl3SecureRwData); /* Map the page tables. */ - SetL3BlockEntry(l3, util::AlignDown(MemoryRegionVirtualTzramL1PageTable.GetAddress(), PageSize), util::AlignDown(MemoryRegionPhysicalTzramL1PageTable.GetAddress(), PageSize), PageSize, MappingAttributesEl3SecureDevice); + SetL3BlockEntry(l3, util::AlignDown(MemoryRegionVirtualTzramL1PageTable.GetAddress(), PageSize), util::AlignDown(MemoryRegionPhysicalTzramL1PageTable.GetAddress(), PageSize), PageSize, MappingAttributesEl3SecureRwData); SetL3BlockEntry(l3, MemoryRegionVirtualTzramL2L3PageTable.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), MemoryRegionVirtualTzramL2L3PageTable.GetSize(), MappingAttributesEl3SecureRwData); } } diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp index 68338212d..a9e6181c5 100644 --- a/exosphere2/program/source/secmon_error.cpp +++ b/exosphere2/program/source/secmon_error.cpp @@ -19,6 +19,26 @@ namespace ams::diag { void AbortImpl() { + /* TODO: This is here for debugging. Remove this when exo2 is working. */ +#if 1 + { + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0xDDDDDDDD; + + u64 temp_reg; + __asm__ __volatile__("mov %0, lr" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast<u32>(temp_reg >> 32); + + + __asm__ __volatile__("mov %0, sp" : "=r"(temp_reg) :: "memory"); + for (int i = 0; i < 0x100; i += 4) { + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x20 + i) = *(volatile u32 *)(temp_reg + i); + } + *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + } +#endif + secmon::SetError(pkg1::ErrorInfo_UnknownAbort); secmon::ErrorReboot(); } @@ -36,6 +56,42 @@ namespace ams::secmon { } NORETURN void ErrorReboot() { + /* TODO: This is here for debugging. Remove this when exo2 is working. */ +#if 1 + { + u64 temp_reg; + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0x5A5A5A5A; + + __asm__ __volatile__("mrs %0, esr_el3" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x08) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x0C) = static_cast<u32>(temp_reg >> 32); + + __asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x1C) = static_cast<u32>(temp_reg >> 32); + + __asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x1C) = static_cast<u32>(temp_reg >> 32); + + __asm__ __volatile__("mov %0, lr" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x20) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x24) = static_cast<u32>(temp_reg >> 32); + + __asm__ __volatile__("mov %0, sp" : "=r"(temp_reg) :: "memory"); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x30) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x34) = static_cast<u32>(temp_reg >> 32); + + for (int i = 0; i < 0x100; i += 4) { + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x40 + i) = *(volatile u32 *)(temp_reg + i); + } + *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } +#endif + /* Lockout the security engine. */ se::Lockout(); diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index 229bd121b..f3bd02792 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -21,6 +21,9 @@ namespace ams::secmon { namespace { + constexpr inline const uintptr_t BootCodeAddress = MemoryRegionVirtualTzramBootCode.GetAddress(); + constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize(); + using namespace ams::mmu; constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { @@ -39,6 +42,20 @@ namespace ams::secmon { InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); } + void ClearLow(uintptr_t address, size_t size) { + /* Clear the low part. */ + util::ClearMemory(reinterpret_cast<void *>(address), size / 2); + } + + void ClearHigh(uintptr_t address, size_t size) { + /* Clear the high part. */ + util::ClearMemory(reinterpret_cast<void *>(address + size / 2), size / 2); + } + + } + + void ClearBootCodeHigh() { + ClearHigh(BootCodeAddress, BootCodeSize); } void UnmapBootCode() { @@ -46,15 +63,11 @@ namespace ams::secmon { u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); - /* Get the boot code region. */ - const uintptr_t boot_code = MemoryRegionVirtualTzramBootCode.GetAddress(); - const size_t boot_code_size = MemoryRegionVirtualTzramBootCode.GetSize(); - - /* Clear the boot code. */ - util::ClearMemory(reinterpret_cast<void *>(boot_code), boot_code_size); + /* Clear the low boot code region; high was already cleared by a previous call. */ + ClearLow(BootCodeAddress, BootCodeSize); /* Unmap. */ - UnmapBootCodeImpl(l1, l2_l3, l2_l3, boot_code, boot_code_size); + UnmapBootCodeImpl(l1, l2_l3, l2_l3, BootCodeAddress, BootCodeSize); /* Ensure the mappings are consistent. */ secmon::EnsureMappingConsistency(); diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index f6fc4a84f..833e98b9a 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -52,7 +52,7 @@ namespace ams::secmon { constinit const se::StickyBits ExpectedSeStickyBits = { .se_security = (1 << 0), /* SE_HARD_SETTING */ .tzram_security = 0, - .crypto_security_perkey = 0, + .crypto_security_perkey = (1 << pkg1::AesKeySlot_UserEnd) - 1, .crypto_keytable_access = { (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ @@ -60,15 +60,15 @@ namespace ams::secmon { (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ - (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 6: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ - (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 7: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 6: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 7: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ - (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 14: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 14: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ }, .rsa_security_perkey = 0, diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 1356f94ab..0727929ac 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -24,7 +24,7 @@ _ZN3ams6secmon5StartEv: mov sp, x20 /* Set SPSEL 0 stack pointer to a temporary location in volatile memory. */ - msr spsel, #1 + msr spsel, #0 ldr x20, =0x1F01C0800 mov sp, x20 @@ -34,17 +34,20 @@ _ZN3ams6secmon5StartEv: /* Invoke main. */ bl _ZN3ams6secmon4MainEv + /* Clear boot code high. */ + bl _ZN3ams6secmon17ClearBootCodeHighEv + /* Set the stack pointer to the core 3 exception stack address. */ ldr x20, =0x1F01F9000 mov sp, x20 + /* Unmap the boot code region (and clear the low part). */ + bl _ZN3ams6secmon13UnmapBootCodeEv + /* Initialize the random cache. */ /* NOTE: Nintendo does this much earlier, but we reuse volatile space. */ bl _ZN3ams6secmon3smc15FillRandomCacheEv - /* Unmap the boot code region. */ - bl _ZN3ams6secmon13UnmapBootCodeEv - /* Jump to lower exception level. */ b _ZN3ams6secmon25JumpToLowerExceptionLevelEv @@ -169,7 +172,6 @@ _ZN3ams6secmon25ReleaseCommonSmcStackLockEv: /* Return. */ ret - ret .section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits .align 4 diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 75bfa4357..346af6f3d 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -228,6 +228,21 @@ namespace ams::secmon::smc { /* Set the invocation result. */ args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args)); + +/* TODO: For debugging. Remove this when exo2 is complete. */ +#if 1 + if (args.r[0] == static_cast<u64>(SmcResult::NotImplemented)) { + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB; + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id); + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; + } + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } +#endif } } diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp index ada818af0..07fea9c43 100644 --- a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp @@ -201,22 +201,29 @@ namespace ams::mmu::arch::arm64 { return ((address >> L3EntryShift) & TableEntryIndexMask); } - constexpr ALWAYS_INLINE void SetTableImpl(u64 *table, u64 index, u64 value) { - /* Ensure (for constexpr validation purposes) that the entry we set is clear. */ - if (table[index]) { - __builtin_unreachable(); - } - - /* Set the value. */ + constexpr ALWAYS_INLINE void SetTableEntryImpl(volatile u64 *table, u64 index, u64 value) { + /* Write the value. */ table[index] = value; } + constexpr ALWAYS_INLINE void SetTableEntry(u64 *table, u64 index, u64 value) { + /* Ensure (for constexpr validation purposes) that the entry we set is clear. */ + if (std::is_constant_evaluated()) { + if (table[index]) { + __builtin_unreachable(); + } + } + + /* Set the value. */ + SetTableEntryImpl(table, index, value); + } + constexpr void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { - SetTableImpl(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + SetTableEntry(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); } constexpr void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { - SetTableImpl(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + SetTableEntry(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); } constexpr void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { @@ -224,7 +231,7 @@ namespace ams::mmu::arch::arm64 { const u64 count = (size >> L1EntryShift); for (u64 i = 0; i < count; ++i) { - SetTableImpl(table, start + i, MakeL1BlockEntry((phys_addr & L1EntryMask) + (i << L1EntryShift), attr)); + SetTableEntry(table, start + i, MakeL1BlockEntry((phys_addr & L1EntryMask) + (i << L1EntryShift), attr)); } } @@ -233,7 +240,7 @@ namespace ams::mmu::arch::arm64 { const u64 count = (size >> L2EntryShift); for (u64 i = 0; i < count; ++i) { - SetTableImpl(table, start + i, MakeL2BlockEntry((phys_addr & L2EntryMask) + (i << L2EntryShift), attr)); + SetTableEntry(table, start + i, MakeL2BlockEntry((phys_addr & L2EntryMask) + (i << L2EntryShift), attr)); } } @@ -242,11 +249,11 @@ namespace ams::mmu::arch::arm64 { const u64 count = (size >> L3EntryShift); for (u64 i = 0; i < count; ++i) { - SetTableImpl(table, start + i, MakeL3BlockEntry((phys_addr & L3EntryMask) + (i << L3EntryShift), attr)); + SetTableEntry(table, start + i, MakeL3BlockEntry((phys_addr & L3EntryMask) + (i << L3EntryShift), attr)); } } - constexpr void InvalidateL1Entries(u64 *table, uintptr_t virt_addr, size_t size) { + constexpr void InvalidateL1Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL1EntryIndex(virt_addr); const u64 count = (size >> L1EntryShift); const u64 end = start + count; @@ -256,7 +263,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void InvalidateL2Entries(u64 *table, uintptr_t virt_addr, size_t size) { + constexpr void InvalidateL2Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL2EntryIndex(virt_addr); const u64 count = (size >> L2EntryShift); const u64 end = start + count; @@ -266,7 +273,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void InvalidateL3Entries(u64 *table, uintptr_t virt_addr, size_t size) { + constexpr void InvalidateL3Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL3EntryIndex(virt_addr); const u64 count = (size >> L3EntryShift); const u64 end = start + count; diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp index bf8515650..5b2c5bdcb 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -30,17 +30,17 @@ namespace ams::secmon { EmummcConfiguration emummc_cfg; u8 _raw_emummc_config[0x120]; }; - union { - u8 _misc_data[0x400 - sizeof(_raw_exosphere_config) - sizeof(_raw_emummc_config)]; - }; u8 sealed_device_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; u8 sealed_master_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; pkg1::BootConfig boot_config; u8 rsa_private_exponents[4][se::RsaSize]; + union { + u8 _misc_data[0xFC0 - sizeof(_raw_exosphere_config) - sizeof(_raw_emummc_config) - sizeof(sealed_device_keys) - sizeof(sealed_master_keys) - sizeof(boot_config) - sizeof(rsa_private_exponents)]; + }; + /* u8 l1_page_table[0x40]; */ }; - static_assert(sizeof(ConfigurationContext) == 0x1000); + static_assert(sizeof(ConfigurationContext) == 0xFC0); static_assert(util::is_pod<ConfigurationContext>::value); - static_assert(offsetof(ConfigurationContext, sealed_device_keys) == 0x400); namespace impl { diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp index dbe3cbcc7..35b18fcda 100644 --- a/libraries/libexosphere/source/se/se_execute.cpp +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -101,14 +101,14 @@ namespace ams::se { void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) { /* Validate sizes. */ AMS_ABORT_UNLESS(dst_size <= AesBlockSize); - AMS_ABORT_UNLESS(src_size == AesBlockSize); + AMS_ABORT_UNLESS(src_size <= AesBlockSize); /* Set the block count to 1. */ reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); /* Create an aligned buffer. */ util::AlignedBuffer<hw::DataCacheLineSize, AesBlockSize> aligned; - std::memcpy(aligned, src, AesBlockSize); + std::memcpy(aligned, src, src_size); hw::FlushDataCache(aligned, AesBlockSize); hw::DataSynchronizationBarrierInnerShareable(); diff --git a/libraries/libexosphere/source/se/se_management.cpp b/libraries/libexosphere/source/se/se_management.cpp index de845c2f0..8c145fd59 100644 --- a/libraries/libexosphere/source/se/se_management.cpp +++ b/libraries/libexosphere/source/se/se_management.cpp @@ -62,12 +62,6 @@ namespace ams::se { void SetPerKeySecure() { auto *SE = GetRegisters(); - /* Clear AES PerKey security. */ - SE->SE_CRYPTO_SECURITY_PERKEY = 0; - - /* Clear RSA PerKey security. */ - SE->SE_RSA_SECURITY_PERKEY = 0; - /* Update PERKEY_SETTING to secure. */ reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE)); } diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp index 03473deb4..ca5134a35 100644 --- a/libraries/libexosphere/source/se/se_suspend.cpp +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -44,8 +44,8 @@ namespace ams::se { if (!TestRegister(SE->SE_CRYPTO_KEYTABLE_ACCESS[i], bits.crypto_keytable_access[i])) { return false; } } - /* Test RSA_SCEURITY_PERKEY */ - if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.rsa_security_perkey)) { return false; } + /* Test RSA_SECURITY_PERKEY */ + if (!TestRegister(SE->SE_RSA_SECURITY_PERKEY, bits.rsa_security_perkey)) { return false; } /* Check RSA_KEYTABLE_ACCESS. */ for (int i = 0; i < RsaKeySlotCount; ++i) { From e3eadcd2e368430fbcb433d2244e28a304a048f9 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 14 May 2020 15:57:22 -0700 Subject: [PATCH 059/118] exo2: Implement SmcReadWriteRegister --- .../source/smc/secmon_define_access_table.inc | 30 +++ .../smc/secmon_define_mc01_access_table.inc | 25 ++ .../smc/secmon_define_mc_access_table.inc | 25 ++ .../smc/secmon_define_pmc_access_table.inc | 25 ++ .../smc/secmon_mc01_access_table_data.inc | 43 ++++ .../smc/secmon_mc_access_table_data.inc | 195 ++++++++++++++++ .../smc/secmon_pmc_access_table_data.inc | 47 ++++ .../program/source/smc/secmon_smc_aes.cpp | 12 +- .../program/source/smc/secmon_smc_aes.hpp | 12 +- .../source/smc/secmon_smc_carveout.cpp | 2 +- .../source/smc/secmon_smc_carveout.hpp | 2 +- .../smc/secmon_smc_device_unique_data.cpp | 8 +- .../smc/secmon_smc_device_unique_data.hpp | 8 +- .../program/source/smc/secmon_smc_error.cpp | 2 +- .../program/source/smc/secmon_smc_error.hpp | 2 +- .../program/source/smc/secmon_smc_es.cpp | 4 +- .../program/source/smc/secmon_smc_es.hpp | 4 +- .../program/source/smc/secmon_smc_handler.cpp | 2 +- .../program/source/smc/secmon_smc_handler.hpp | 2 +- .../program/source/smc/secmon_smc_info.cpp | 8 +- .../program/source/smc/secmon_smc_info.hpp | 8 +- .../source/smc/secmon_smc_memory_access.cpp | 4 +- .../source/smc/secmon_smc_memory_access.hpp | 4 +- .../smc/secmon_smc_power_management.cpp | 6 +- .../smc/secmon_smc_power_management.hpp | 6 +- .../program/source/smc/secmon_smc_random.cpp | 4 +- .../program/source/smc/secmon_smc_random.hpp | 4 +- .../source/smc/secmon_smc_register_access.cpp | 176 +++++++++++++- .../source/smc/secmon_smc_register_access.hpp | 2 +- .../program/source/smc/secmon_smc_result.cpp | 4 +- .../program/source/smc/secmon_smc_result.hpp | 4 +- .../program/source/smc/secmon_smc_rsa.cpp | 4 +- .../program/source/smc/secmon_smc_rsa.hpp | 4 +- .../include/exosphere/tegra/tegra_mc.hpp | 214 +++++++++++++++++- .../include/exosphere/tegra/tegra_pmc.hpp | 155 +++++++------ 35 files changed, 918 insertions(+), 139 deletions(-) create mode 100644 exosphere2/program/source/smc/secmon_define_access_table.inc create mode 100644 exosphere2/program/source/smc/secmon_define_mc01_access_table.inc create mode 100644 exosphere2/program/source/smc/secmon_define_mc_access_table.inc create mode 100644 exosphere2/program/source/smc/secmon_define_pmc_access_table.inc create mode 100644 exosphere2/program/source/smc/secmon_mc01_access_table_data.inc create mode 100644 exosphere2/program/source/smc/secmon_mc_access_table_data.inc create mode 100644 exosphere2/program/source/smc/secmon_pmc_access_table_data.inc diff --git a/exosphere2/program/source/smc/secmon_define_access_table.inc b/exosphere2/program/source/smc/secmon_define_access_table.inc new file mode 100644 index 000000000..d8875231b --- /dev/null +++ b/exosphere2/program/source/smc/secmon_define_access_table.inc @@ -0,0 +1,30 @@ +/* + * 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/>. + */ + +using __ACCESS_TABLE_NAME__ = AccessTable<__ACCESS_TABLE_ADDRESS__, [] { + /* Declare a table. */ + std::array<u8, 0x80> table = {}; + + /* Declare a helper. */ + auto SetRegisterAllowed = [&](uintptr_t reg) { SetRegisterTableAllowed(table, reg); }; + + /* Populate the table. */ + #include __ACCESS_TABLE_INC__ + + return table; +}()>; + +static_assert(__ACCESS_TABLE_NAME__::Address >= __ACCESS_TABLE_ADDRESS__); diff --git a/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc b/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc new file mode 100644 index 000000000..ecb142b4b --- /dev/null +++ b/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc @@ -0,0 +1,25 @@ +/* + * 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/>. + */ + +#define __ACCESS_TABLE_NAME__ Mc01AccessTable +#define __ACCESS_TABLE_ADDRESS__ 0 +#define __ACCESS_TABLE_INC__ "secmon_mc01_access_table_data.inc" + +#include "secmon_define_access_table.inc" + +#undef __ACCESS_TABLE_INC__ +#undef __ACCESS_TABLE_ADDRESS__ +#undef __ACCESS_TABLE_NAME__ diff --git a/exosphere2/program/source/smc/secmon_define_mc_access_table.inc b/exosphere2/program/source/smc/secmon_define_mc_access_table.inc new file mode 100644 index 000000000..bfc3da5db --- /dev/null +++ b/exosphere2/program/source/smc/secmon_define_mc_access_table.inc @@ -0,0 +1,25 @@ +/* + * 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/>. + */ + +#define __ACCESS_TABLE_NAME__ McAccessTable +#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceMemoryController.GetAddress() +#define __ACCESS_TABLE_INC__ "secmon_mc_access_table_data.inc" + +#include "secmon_define_access_table.inc" + +#undef __ACCESS_TABLE_INC__ +#undef __ACCESS_TABLE_ADDRESS__ +#undef __ACCESS_TABLE_NAME__ diff --git a/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc b/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc new file mode 100644 index 000000000..9ce0f5236 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc @@ -0,0 +1,25 @@ +/* + * 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/>. + */ + +#define __ACCESS_TABLE_NAME__ PmcAccessTable +#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDevicePmc.GetAddress() +#define __ACCESS_TABLE_INC__ "secmon_pmc_access_table_data.inc" + +#include "secmon_define_access_table.inc" + +#undef __ACCESS_TABLE_INC__ +#undef __ACCESS_TABLE_ADDRESS__ +#undef __ACCESS_TABLE_NAME__ diff --git a/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc b/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc new file mode 100644 index 000000000..4e4f68d1f --- /dev/null +++ b/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc @@ -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/>. + */ + +SetRegisterAllowed(MC_STAT_CONTROL); /* 0x100 */ +SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT); /* 0x108 */ +SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT_MSBS); /* 0x10C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO); /* 0x118 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI); /* 0x11C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_SPARE); /* 0x124 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_0); /* 0x128 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_1); /* 0x12C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_2); /* 0x130 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_3); /* 0x134 */ +SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT); /* 0x138 */ +SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT_MSBS); /* 0x13C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO); /* 0x158 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI); /* 0x15C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_SPARE); /* 0x164 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_0); /* 0x168 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_1); /* 0x16C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_2); /* 0x170 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_3); /* 0x174 */ +SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT); /* 0x178 */ +SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT_MSBS); /* 0x17C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER); /* 0xA20 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER); /* 0xA24 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_4); /* 0xB88 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_4); /* 0xB8C */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_5); /* 0xBC4 */ +SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_5); /* 0xBC8 */ \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_mc_access_table_data.inc b/exosphere2/program/source/smc/secmon_mc_access_table_data.inc new file mode 100644 index 000000000..5b97b8f3f --- /dev/null +++ b/exosphere2/program/source/smc/secmon_mc_access_table_data.inc @@ -0,0 +1,195 @@ +/* + * 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/>. + */ + +SetRegisterAllowed(MC_INTSTATUS); /* 0x000 */ +SetRegisterAllowed(MC_INTMASK); /* 0x004 */ +SetRegisterAllowed(MC_ERR_STATUS); /* 0x008 */ +SetRegisterAllowed(MC_ERR_ADR); /* 0x00C */ +SetRegisterAllowed(MC_SMMU_CONFIG); /* 0x010 */ +SetRegisterAllowed(MC_SMMU_PTB_ASID); /* 0x01C */ +SetRegisterAllowed(MC_SMMU_PTB_DATA); /* 0x020 */ +SetRegisterAllowed(MC_SMMU_TLB_FLUSH); /* 0x030 */ +SetRegisterAllowed(MC_SMMU_PTC_FLUSH); /* 0x034 */ +SetRegisterAllowed(MC_EMEM_CFG); /* 0x050 */ +SetRegisterAllowed(MC_EMEM_ADR_CFG); /* 0x054 */ +SetRegisterAllowed(MC_EMEM_ARB_CFG); /* 0x090 */ +SetRegisterAllowed(MC_EMEM_ARB_OUTSTANDING_REQ); /* 0x094 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RCD); /* 0x098 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RP); /* 0x09C */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RC); /* 0x0A0 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAS); /* 0x0A4 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_FAW); /* 0x0A8 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RRD); /* 0x0AC */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAP2PRE); /* 0x0B0 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_WAP2PRE); /* 0x0B4 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2R); /* 0x0B8 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2W); /* 0x0BC */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2W); /* 0x0C0 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2R); /* 0x0C4 */ +SetRegisterAllowed(MC_EMEM_ARB_MISC2); /* 0x0C8 */ +SetRegisterAllowed(MC_EMEM_ARB_DA_TURNS); /* 0x0D0 */ +SetRegisterAllowed(MC_EMEM_ARB_DA_COVERS); /* 0x0D4 */ +SetRegisterAllowed(MC_EMEM_ARB_MISC0); /* 0x0D8 */ +SetRegisterAllowed(MC_EMEM_ARB_MISC1); /* 0x0DC */ +SetRegisterAllowed(MC_EMEM_ARB_RING1_THROTTLE); /* 0x0E0 */ +SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL); /* 0x200 */ +SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS); /* 0x204 */ +SetRegisterAllowed(MC_SMMU_AFI_ASID); /* 0x238 */ +SetRegisterAllowed(MC_SMMU_DC_ASID); /* 0x240 */ +SetRegisterAllowed(MC_SMMU_DCB_ASID); /* 0x244 */ +SetRegisterAllowed(MC_SMMU_HC_ASID); /* 0x250 */ +SetRegisterAllowed(MC_SMMU_HDA_ASID); /* 0x254 */ +SetRegisterAllowed(MC_SMMU_ISP2_ASID); /* 0x258 */ +SetRegisterAllowed(MC_SMMU_NVENC_ASID); /* 0x264 */ +SetRegisterAllowed(MC_SMMU_NV_ASID); /* 0x268 */ +SetRegisterAllowed(MC_SMMU_NV2_ASID); /* 0x26C */ +SetRegisterAllowed(MC_SMMU_PPCS_ASID); /* 0x270 */ +SetRegisterAllowed(MC_SMMU_SATA_ASID); /* 0x274 */ +SetRegisterAllowed(MC_SMMU_VI_ASID); /* 0x280 */ +SetRegisterAllowed(MC_SMMU_VIC_ASID); /* 0x284 */ +SetRegisterAllowed(MC_SMMU_XUSB_HOST_ASID); /* 0x288 */ +SetRegisterAllowed(MC_SMMU_XUSB_DEV_ASID); /* 0x28C */ +SetRegisterAllowed(MC_SMMU_TSEC_ASID); /* 0x294 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_AVPC_0); /* 0x2E4 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_0); /* 0x2E8 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_1); /* 0x2EC */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_0); /* 0x2F4 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_1); /* 0x2F8 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_0); /* 0x310 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_1); /* 0x314 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_MPCORE_0); /* 0x320 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVENC_0); /* 0x328 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_0); /* 0x344 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_1); /* 0x348 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_0); /* 0x370 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_1); /* 0x374 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_0); /* 0x37C */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_1); /* 0x380 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_TSEC_0); /* 0x390 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VIC_0); /* 0x394 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VI2_0); /* 0x398 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU_0); /* 0x3AC */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCA_0); /* 0x3B8 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAA_0); /* 0x3BC */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMC_0); /* 0x3C0 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAB_0); /* 0x3C4 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVDEC_0); /* 0x3D8 */ +SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU2_0); /* 0x3E8 */ +SetRegisterAllowed(MC_DIS_PTSA_RATE); /* 0x41C */ +SetRegisterAllowed(MC_DIS_PTSA_MIN); /* 0x420 */ +SetRegisterAllowed(MC_DIS_PTSA_MAX); /* 0x424 */ +SetRegisterAllowed(MC_DISB_PTSA_RATE); /* 0x428 */ +SetRegisterAllowed(MC_DISB_PTSA_MIN); /* 0x42C */ +SetRegisterAllowed(MC_DISB_PTSA_MAX); /* 0x430 */ +SetRegisterAllowed(MC_VE_PTSA_RATE); /* 0x434 */ +SetRegisterAllowed(MC_VE_PTSA_MIN); /* 0x438 */ +SetRegisterAllowed(MC_VE_PTSA_MAX); /* 0x43C */ +SetRegisterAllowed(MC_MLL_MPCORER_PTSA_RATE); /* 0x44C */ +SetRegisterAllowed(MC_RING1_PTSA_RATE); /* 0x47C */ +SetRegisterAllowed(MC_RING1_PTSA_MIN); /* 0x480 */ +SetRegisterAllowed(MC_RING1_PTSA_MAX); /* 0x484 */ +SetRegisterAllowed(MC_PCX_PTSA_RATE); /* 0x4AC */ +SetRegisterAllowed(MC_PCX_PTSA_MIN); /* 0x4B0 */ +SetRegisterAllowed(MC_PCX_PTSA_MAX); /* 0x4B4 */ +SetRegisterAllowed(MC_MSE_PTSA_RATE); /* 0x4C4 */ +SetRegisterAllowed(MC_MSE_PTSA_MIN); /* 0x4C8 */ +SetRegisterAllowed(MC_MSE_PTSA_MAX); /* 0x4CC */ +SetRegisterAllowed(MC_AHB_PTSA_RATE); /* 0x4DC */ +SetRegisterAllowed(MC_AHB_PTSA_MIN); /* 0x4E0 */ +SetRegisterAllowed(MC_AHB_PTSA_MAX); /* 0x4E4 */ +SetRegisterAllowed(MC_APB_PTSA_RATE); /* 0x4E8 */ +SetRegisterAllowed(MC_APB_PTSA_MIN); /* 0x4EC */ +SetRegisterAllowed(MC_APB_PTSA_MAX); /* 0x4F0 */ +SetRegisterAllowed(MC_FTOP_PTSA_RATE); /* 0x50C */ +SetRegisterAllowed(MC_HOST_PTSA_RATE); /* 0x518 */ +SetRegisterAllowed(MC_HOST_PTSA_MIN); /* 0x51C */ +SetRegisterAllowed(MC_HOST_PTSA_MAX); /* 0x520 */ +SetRegisterAllowed(MC_USBX_PTSA_RATE); /* 0x524 */ +SetRegisterAllowed(MC_USBX_PTSA_MIN); /* 0x528 */ +SetRegisterAllowed(MC_USBX_PTSA_MAX); /* 0x52C */ +SetRegisterAllowed(MC_USBD_PTSA_RATE); /* 0x530 */ +SetRegisterAllowed(MC_USBD_PTSA_MIN); /* 0x534 */ +SetRegisterAllowed(MC_USBD_PTSA_MAX); /* 0x538 */ +SetRegisterAllowed(MC_GK_PTSA_RATE); /* 0x53C */ +SetRegisterAllowed(MC_GK_PTSA_MIN); /* 0x540 */ +SetRegisterAllowed(MC_GK_PTSA_MAX); /* 0x544 */ +SetRegisterAllowed(MC_AUD_PTSA_RATE); /* 0x548 */ +SetRegisterAllowed(MC_AUD_PTSA_MIN); /* 0x54C */ +SetRegisterAllowed(MC_AUD_PTSA_MAX); /* 0x550 */ +SetRegisterAllowed(MC_VICPC_PTSA_RATE); /* 0x554 */ +SetRegisterAllowed(MC_VICPC_PTSA_MIN); /* 0x558 */ +SetRegisterAllowed(MC_VICPC_PTSA_MAX); /* 0x55C */ +SetRegisterAllowed(MC_JPG_PTSA_RATE); /* 0x584 */ +SetRegisterAllowed(MC_JPG_PTSA_MIN); /* 0x588 */ +SetRegisterAllowed(MC_JPG_PTSA_MAX); /* 0x58C */ +SetRegisterAllowed(MC_GK2_PTSA_RATE); /* 0x610 */ +SetRegisterAllowed(MC_GK2_PTSA_MIN); /* 0x614 */ +SetRegisterAllowed(MC_GK2_PTSA_MAX); /* 0x618 */ +SetRegisterAllowed(MC_SDM_PTSA_RATE); /* 0x61C */ +SetRegisterAllowed(MC_SDM_PTSA_MIN); /* 0x620 */ +SetRegisterAllowed(MC_SDM_PTSA_MAX); /* 0x624 */ +SetRegisterAllowed(MC_HDAPC_PTSA_RATE); /* 0x628 */ +SetRegisterAllowed(MC_HDAPC_PTSA_MIN); /* 0x62C */ +SetRegisterAllowed(MC_HDAPC_PTSA_MAX); /* 0x630 */ +SetRegisterAllowed(MC_SEC_CARVEOUT_BOM); /* 0x670 */ +SetRegisterAllowed(MC_SEC_CARVEOUT_SIZE_MB); /* 0x674 */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A); /* 0x690 */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB); /* 0x694 */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B); /* 0x698 */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB); /* 0x69C */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C); /* 0x6A0 */ +SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB); /* 0x6A4 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_RFCPB); /* 0x6C0 */ +SetRegisterAllowed(MC_EMEM_ARB_TIMING_CCDMW); /* 0x6C4 */ +SetRegisterAllowed(MC_EMEM_ARB_REFPB_HP_CTRL); /* 0x6F0 */ +SetRegisterAllowed(MC_EMEM_ARB_REFPB_BANK_CTRL); /* 0x6F4 */ +SetRegisterAllowed(MC_PTSA_GRANT_DECREMENT); /* 0x960 */ +SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL_1); /* 0x970 */ +SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS_1); /* 0x974 */ +SetRegisterAllowed(MC_SMMU_PTC_FLUSH_1); /* 0x9B8 */ +SetRegisterAllowed(MC_SMMU_DC1_ASID); /* 0xA88 */ +SetRegisterAllowed(MC_SMMU_SDMMC1A_ASID); /* 0xA94 */ +SetRegisterAllowed(MC_SMMU_SDMMC2A_ASID); /* 0xA98 */ +SetRegisterAllowed(MC_SMMU_SDMMC3A_ASID); /* 0xA9C */ +SetRegisterAllowed(MC_SMMU_SDMMC4A_ASID); /* 0xAA0 */ +SetRegisterAllowed(MC_SMMU_ISP2B_ASID); /* 0xAA4 */ +SetRegisterAllowed(MC_SMMU_GPU_ASID); /* 0xAA8 */ +SetRegisterAllowed(MC_SMMU_GPUB_ASID); /* 0xAAC */ +SetRegisterAllowed(MC_SMMU_PPCS2_ASID); /* 0xAB0 */ +SetRegisterAllowed(MC_SMMU_NVDEC_ASID); /* 0xAB4 */ +SetRegisterAllowed(MC_SMMU_APE_ASID); /* 0xAB8 */ +SetRegisterAllowed(MC_SMMU_SE_ASID); /* 0xABC */ +SetRegisterAllowed(MC_SMMU_NVJPG_ASID); /* 0xAC0 */ +SetRegisterAllowed(MC_SMMU_HC1_ASID); /* 0xAC4 */ +SetRegisterAllowed(MC_SMMU_SE1_ASID); /* 0xAC8 */ +SetRegisterAllowed(MC_SMMU_AXIAP_ASID); /* 0xACC */ +SetRegisterAllowed(MC_SMMU_ETR_ASID); /* 0xAD0 */ +SetRegisterAllowed(MC_SMMU_TSECB_ASID); /* 0xAD4 */ +SetRegisterAllowed(MC_SMMU_TSEC1_ASID); /* 0xAD8 */ +SetRegisterAllowed(MC_SMMU_TSECB1_ASID); /* 0xADC */ +SetRegisterAllowed(MC_SMMU_NVDEC1_ASID); /* 0xAE0 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_CTRL); /* 0xBCC */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0); /* 0xBD0 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1); /* 0xBD4 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2); /* 0xBD8 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3); /* 0xBDC */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4); /* 0xBE0 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5); /* 0xBE4 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6); /* 0xBE8 */ +SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7); /* 0xBEC */ +SetRegisterAllowed(MC_ERR_GENERALIZED_CARVEOUT_STATUS); /* 0xC00 */ +SetRegisterAllowed(MC_SECURITY_CARVEOUT2_BOM); /* 0xC5C */ +SetRegisterAllowed(MC_SECURITY_CARVEOUT3_BOM); /* 0xCAC */ \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc b/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc new file mode 100644 index 000000000..e94be6d8d --- /dev/null +++ b/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc @@ -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/>. + */ + +SetRegisterAllowed(APBDEV_PMC_CNTRL); /* 0x000 */ +SetRegisterAllowed(APBDEV_PMC_WAKE_MASK); /* 0x00C */ +SetRegisterAllowed(APBDEV_PMC_WAKE_LVL); /* 0x010 */ +SetRegisterAllowed(APBDEV_PMC_WAKE_STATUS); /* 0x014 */ +SetRegisterAllowed(APBDEV_PMC_DPD_PADS_ORIDE); /* 0x01C */ +SetRegisterAllowed(APBDEV_PMC_DPD_SAMPLE); /* 0x020 */ +SetRegisterAllowed(APBDEV_PMC_CLAMP_STATUS); /* 0x02C */ +SetRegisterAllowed(APBDEV_PMC_PWRGATE_TOGGLE); /* 0x030 */ +SetRegisterAllowed(APBDEV_PMC_REMOVE_CLAMPING_CMD ); /* 0x034 */ +SetRegisterAllowed(APBDEV_PMC_PWRGATE_STATUS); /* 0x038 */ +SetRegisterAllowed(APBDEV_PMC_PWRGOOD_TIMER); /* 0x03C */ +SetRegisterAllowed(APBDEV_PMC_BLINK_TIMER); /* 0x040 */ +SetRegisterAllowed(APBDEV_PMC_NO_IOPOWER); /* 0x044 */ +SetRegisterAllowed(APBDEV_PMC_PWR_DET); /* 0x048 */ +SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE_LVL_MASK); /* 0x0DC */ +SetRegisterAllowed(APBDEV_PMC_WAKE_DELAY); /* 0x0E0 */ +SetRegisterAllowed(APBDEV_PMC_PWR_DET_VAL); /* 0x0E4 */ +SetRegisterAllowed(APBDEV_PMC_WAKE2_MASK); /* 0x160 */ +SetRegisterAllowed(APBDEV_PMC_WAKE2_LVL); /* 0x164 */ +SetRegisterAllowed(APBDEV_PMC_WAKE2_STATUS); /* 0x168 */ +SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE2_LVL_MASK ); /* 0x170 */ +SetRegisterAllowed(APBDEV_PMC_CLK_OUT_CNTRL); /* 0x1A8 */ +SetRegisterAllowed(APBDEV_PMC_IO_DPD_REQ); /* 0x1B8 */ +SetRegisterAllowed(APBDEV_PMC_IO_DPD_STATUS); /* 0x1BC */ +SetRegisterAllowed(APBDEV_PMC_IO_DPD2_REQ); /* 0x1C0 */ +SetRegisterAllowed(APBDEV_PMC_IO_DPD2_STATUS); /* 0x1C4 */ +SetRegisterAllowed(APBDEV_PMC_SEL_DPD_TIM); /* 0x1C8 */ +SetRegisterAllowed(APBDEV_PMC_TSC_MULT); /* 0x2B4 */ +SetRegisterAllowed(APBDEV_PMC_GPU_RG_CNTRL); /* 0x2D4 */ +SetRegisterAllowed(APBDEV_PMC_CNTRL2); /* 0x440 */ +SetRegisterAllowed(APBDEV_PMC_WAKE_DEBOUNCE_EN); /* 0x4D8 */ \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 9283a278c..2edd1d812 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -19,32 +19,32 @@ namespace ams::secmon::smc { - SmcResult SmcGenerateAesKek(const SmcArguments &args) { + SmcResult SmcGenerateAesKek(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcLoadAesKey(const SmcArguments &args) { + SmcResult SmcLoadAesKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcComputeAes(const SmcArguments &args) { + SmcResult SmcComputeAes(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args) { + SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcComputeCmac(const SmcArguments &args) { + SmcResult SmcComputeCmac(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcLoadPreparedAesKey(const SmcArguments &args) { + SmcResult SmcLoadPreparedAesKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index b0b3c3d95..8aa55fd81 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -19,11 +19,11 @@ namespace ams::secmon::smc { - SmcResult SmcGenerateAesKek(const SmcArguments &args); - SmcResult SmcLoadAesKey(const SmcArguments &args); - SmcResult SmcComputeAes(const SmcArguments &args); - SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args); - SmcResult SmcComputeCmac(const SmcArguments &args); - SmcResult SmcLoadPreparedAesKey(const SmcArguments &args); + SmcResult SmcGenerateAesKek(SmcArguments &args); + SmcResult SmcLoadAesKey(SmcArguments &args); + SmcResult SmcComputeAes(SmcArguments &args); + SmcResult SmcGenerateSpecificAesKey(SmcArguments &args); + SmcResult SmcComputeCmac(SmcArguments &args); + SmcResult SmcLoadPreparedAesKey(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp index ba037d498..2f947431e 100644 --- a/exosphere2/program/source/smc/secmon_smc_carveout.cpp +++ b/exosphere2/program/source/smc/secmon_smc_carveout.cpp @@ -19,7 +19,7 @@ namespace ams::secmon::smc { - SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args) { + SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere2/program/source/smc/secmon_smc_carveout.hpp index f3d346d97..bc09fcd13 100644 --- a/exosphere2/program/source/smc/secmon_smc_carveout.hpp +++ b/exosphere2/program/source/smc/secmon_smc_carveout.hpp @@ -19,6 +19,6 @@ namespace ams::secmon::smc { - SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args); + SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp index 39fb28dcb..b9ac92699 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -19,23 +19,23 @@ namespace ams::secmon::smc { - SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args) { + SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args) { + SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } /* Legacy APIs. */ - SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args) { + SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args) { + SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp index 9def74026..1c36ec1f8 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -19,11 +19,11 @@ namespace ams::secmon::smc { - SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args); - SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args); + SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args); + SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args); /* Legacy APIs. */ - SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args); - SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args); + SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args); + SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere2/program/source/smc/secmon_smc_error.cpp index 309a2e832..70ea8f1db 100644 --- a/exosphere2/program/source/smc/secmon_smc_error.cpp +++ b/exosphere2/program/source/smc/secmon_smc_error.cpp @@ -19,7 +19,7 @@ namespace ams::secmon::smc { - SmcResult SmcShowError(const SmcArguments &args) { + SmcResult SmcShowError(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere2/program/source/smc/secmon_smc_error.hpp index 62eff45da..25b74f1a6 100644 --- a/exosphere2/program/source/smc/secmon_smc_error.hpp +++ b/exosphere2/program/source/smc/secmon_smc_error.hpp @@ -19,6 +19,6 @@ namespace ams::secmon::smc { - SmcResult SmcShowError(const SmcArguments &args); + SmcResult SmcShowError(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp index a707c35f0..939e12b44 100644 --- a/exosphere2/program/source/smc/secmon_smc_es.cpp +++ b/exosphere2/program/source/smc/secmon_smc_es.cpp @@ -19,12 +19,12 @@ namespace ams::secmon::smc { - SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args) { + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcPrepareEsCommonKey(const SmcArguments &args) { + SmcResult SmcPrepareEsCommonKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/exosphere2/program/source/smc/secmon_smc_es.hpp index 1aabbc210..a9d2d30d9 100644 --- a/exosphere2/program/source/smc/secmon_smc_es.hpp +++ b/exosphere2/program/source/smc/secmon_smc_es.hpp @@ -26,7 +26,7 @@ namespace ams::secmon::smc { EsKeyType_Count = 2, }; - SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args); - SmcResult SmcPrepareEsCommonKey(const SmcArguments &args); + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); + SmcResult SmcPrepareEsCommonKey(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 346af6f3d..918824504 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -207,7 +207,7 @@ namespace ams::secmon::smc { return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0; } - SmcResult InvokeSmcHandler(const HandlerInfo &info, const SmcArguments &args) { + SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) { /* Check if the smc is restricted. */ if (AMS_UNLIKELY(IsHandlerRestricted(info))) { return SmcResult::NotPermitted; diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere2/program/source/smc/secmon_smc_handler.hpp index 9c5d4b317..f05120951 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.hpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.hpp @@ -19,6 +19,6 @@ namespace ams::secmon::smc { - using SmcHandler = SmcResult (*)(const SmcArguments &args); + using SmcHandler = SmcResult (*)(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index 2be199d28..fba5f75ad 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -19,23 +19,23 @@ namespace ams::secmon::smc { - SmcResult SmcGetConfigUser(const SmcArguments &args) { + SmcResult SmcGetConfigUser(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcGetConfigKern(const SmcArguments &args) { + SmcResult SmcGetConfigKern(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcSetConfig(const SmcArguments &args) { + SmcResult SmcSetConfig(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } /* This is an atmosphere extension smc. */ - SmcResult SmcGetEmummcConfig(const SmcArguments &args) { + SmcResult SmcGetEmummcConfig(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp index 8a0e3c5d5..907682b8a 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -49,11 +49,11 @@ namespace ams::secmon::smc { ExosphereAllowCalWrites = 65006, }; - SmcResult SmcGetConfigUser(const SmcArguments &args); - SmcResult SmcGetConfigKern(const SmcArguments &args); - SmcResult SmcSetConfig(const SmcArguments &args); + SmcResult SmcGetConfigUser(SmcArguments &args); + SmcResult SmcGetConfigKern(SmcArguments &args); + SmcResult SmcSetConfig(SmcArguments &args); /* This is an atmosphere extension smc. */ - SmcResult SmcGetEmummcConfig(const SmcArguments &args); + SmcResult SmcGetEmummcConfig(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp index 47f29fc8d..d68dcf879 100644 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -20,12 +20,12 @@ namespace ams::secmon::smc { /* This is an atmosphere extension smc. */ - SmcResult SmcIramCopy(const SmcArguments &args) { + SmcResult SmcIramCopy(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcWriteAddress(const SmcArguments &args) { + SmcResult SmcWriteAddress(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp index 2b44fe292..310657601 100644 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp @@ -20,7 +20,7 @@ namespace ams::secmon::smc { /* This is an atmosphere extension smc. */ - SmcResult SmcIramCopy(const SmcArguments &args); - SmcResult SmcWriteAddress(const SmcArguments &args); + SmcResult SmcIramCopy(SmcArguments &args); + SmcResult SmcWriteAddress(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 2bfdaae52..7ccd6ef77 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -96,12 +96,12 @@ namespace ams::secmon::smc { } - SmcResult SmcPowerOffCpu(const SmcArguments &args) { + SmcResult SmcPowerOffCpu(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcPowerOnCpu(const SmcArguments &args) { + SmcResult SmcPowerOnCpu(SmcArguments &args) { /* Get and validate the core to power on. */ const int which_core = args.r[1]; if (!(0 <= which_core && which_core < NumCores)) { @@ -128,7 +128,7 @@ namespace ams::secmon::smc { return SmcResult::PsciSuccess; } - SmcResult SmcSuspendCpu(const SmcArguments &args) { + SmcResult SmcSuspendCpu(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp index bc6f45229..65bd53df2 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.hpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp @@ -19,10 +19,10 @@ namespace ams::secmon::smc { - SmcResult SmcPowerOffCpu(const SmcArguments &args); - SmcResult SmcPowerOnCpu(const SmcArguments &args); + SmcResult SmcPowerOffCpu(SmcArguments &args); + SmcResult SmcPowerOnCpu(SmcArguments &args); - SmcResult SmcSuspendCpu(const SmcArguments &args); + SmcResult SmcSuspendCpu(SmcArguments &args); bool IsChargerHiZModeEnabled(); void SetChargerHiZModeEnabled(bool en); diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp index 283e8d736..693080aa7 100644 --- a/exosphere2/program/source/smc/secmon_smc_random.cpp +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -19,12 +19,12 @@ namespace ams::secmon::smc { - SmcResult SmcGenerateRandomBytes(const SmcArguments &args) { + SmcResult SmcGenerateRandomBytes(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args) { + SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere2/program/source/smc/secmon_smc_random.hpp index 27b20b241..98c299079 100644 --- a/exosphere2/program/source/smc/secmon_smc_random.hpp +++ b/exosphere2/program/source/smc/secmon_smc_random.hpp @@ -19,7 +19,7 @@ namespace ams::secmon::smc { - SmcResult SmcGenerateRandomBytes(const SmcArguments &args); - SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args); + SmcResult SmcGenerateRandomBytes(SmcArguments &args); + SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp index a656174f3..0f3e6e08e 100644 --- a/exosphere2/program/source/smc/secmon_smc_register_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -19,9 +19,179 @@ namespace ams::secmon::smc { - SmcResult SmcReadWriteRegister(const SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + namespace { + + template<size_t N> + constexpr void SetRegisterTableAllowed(std::array<u8, N> &arr, uintptr_t reg) { + /* All registers should be four byte aligned. */ + if (reg % sizeof(u32) != 0) { + __builtin_unreachable(); + } + + /* Reduce the register to an index. */ + reg /= sizeof(u32); + + /* Get the index and mask. */ + const auto index = reg / BITSIZEOF(u8); + const auto mask = (1u << (reg % BITSIZEOF(u8))); + + /* Check that the permission bit isn't already set. */ + if ((arr[index] & mask) != 0) { + __builtin_unreachable(); + } + + /* Set the permission bit. */ + arr[index] |= mask; + + /* Ensure that indices are set in sorted order. */ + for (auto i = (reg % BITSIZEOF(u8)) + 1; i < 8; ++i) { + if ((arr[index] & (1u << i)) != 0) { + __builtin_unreachable(); + } + } + + for (auto i = index + 1; i < arr.size(); ++i) { + if (arr[i] != 0) { + __builtin_unreachable(); + } + } + } + + template<size_t N> + consteval std::pair<size_t, size_t> GetReducedAccessTableInfo(const std::array<u8, N> &arr) { + for (int last = arr.size() - 1; last >= 0; --last) { + if (arr[last] != 0) { + const int end = last + 1; + for (int start = 0; start < end; ++start) { + if (arr[start] != 0) { + return std::make_pair(static_cast<size_t>(start), static_cast<size_t>(end)); + } + } + return std::make_pair(static_cast<size_t>(0), static_cast<size_t>(end)); + } + } + + /* All empty perm table is disallowed. */ + __builtin_unreachable(); + } + + + template<u32 _Address, auto RawTable> + struct AccessTable { + static constexpr inline auto ReducedAccessTableInfo = GetReducedAccessTableInfo(RawTable); + static constexpr inline size_t ReducedAccessTableSize = ReducedAccessTableInfo.second - ReducedAccessTableInfo.first; + static constexpr inline auto ReducedAccessTable = []() -> std::array<u8, ReducedAccessTableSize> { + std::array<u8, ReducedAccessTableSize> reduced = {}; + + for (size_t i = ReducedAccessTableInfo.first; i < ReducedAccessTableInfo.second; ++i) { + reduced[i - ReducedAccessTableInfo.first] = RawTable[i]; + } + + return reduced; + }(); + + static constexpr u32 Address = _Address + (ReducedAccessTableInfo.first * sizeof(u32) * BITSIZEOF(u8)); + static constexpr u32 Size = static_cast<u32>(ReducedAccessTableSize * sizeof(u32) * BITSIZEOF(u8)); + + static_assert(Size <= 0x1000); + }; + + struct AccessTableEntry { + const u8 * const table; + uintptr_t virtual_address; + u32 address; + u32 size; + }; + + /* Include the access tables. */ + #include "secmon_define_pmc_access_table.inc" + #include "secmon_define_mc_access_table.inc" + #include "secmon_define_mc01_access_table.inc" + + constexpr const AccessTableEntry AccessTables[] = { + { PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, }, + { McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, }, + { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, }, + { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, }, + }; + + constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) { + /* Check if the address is within range. */ + if (!(entry.address <= address && address < entry.address + entry.size)) { + return false; + } + + /* Get the offset. */ + const auto offset = address - entry.address; + + /* Convert it to an index. */ + const auto reg_index = offset / sizeof(u32); + + /* Get the bit fields. */ + const auto index = reg_index / BITSIZEOF(u8); + const auto mask = (1u << (reg_index % BITSIZEOF(u8))); + + /* Validate that we're not going out of bounds. */ + if (index >= entry.size / sizeof(u32)) { + return false; + } + + return (entry.table[index] & mask) != 0; + } + + constexpr const AccessTableEntry *GetAccessTableEntry(uintptr_t address) { + for (const auto &entry : AccessTables) { + if (IsAccessAllowed(entry, address)) { + return std::addressof(entry); + } + } + + return nullptr; + } + + } + + SmcResult SmcReadWriteRegister(SmcArguments &args) { + /* Get the arguments. */ + const uintptr_t address = args.r[1]; + const u32 mask = args.r[2]; + const u32 value = args.r[3]; + + /* Validate that the address is aligned. */ + if (!util::IsAligned(address, alignof(u32))) { + return SmcResult::InvalidArgument; + } + + /* Find the access table. */ + const AccessTableEntry * const entry = GetAccessTableEntry(address); + + /* If we have no table, don't perform the write. */ + if (entry == nullptr) { + /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ + /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ + /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ + /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ + constexpr uintptr_t MC = MemoryRegionVirtualDeviceMemoryController.GetAddress(); + if (address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)) { + return SmcResult::Success; + } + + return SmcResult::InvalidArgument; + } + + /* Get the address to read or write. */ + const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); + u32 out = 0; + + if (mask != ~static_cast<u32>(0)) { + out = reg::Read(virtual_address); + } + if (mask != static_cast<u32>(0)) { + reg::Write(virtual_address, (out & ~mask) | (value & mask)); + } + + args.r[1] = out; + return SmcResult::Success; } } diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere2/program/source/smc/secmon_smc_register_access.hpp index d364b27f8..6a74cdc96 100644 --- a/exosphere2/program/source/smc/secmon_smc_register_access.hpp +++ b/exosphere2/program/source/smc/secmon_smc_register_access.hpp @@ -19,6 +19,6 @@ namespace ams::secmon::smc { - SmcResult SmcReadWriteRegister(const SmcArguments &args); + SmcResult SmcReadWriteRegister(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp index 6b17b8e56..5223ceca5 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -19,12 +19,12 @@ namespace ams::secmon::smc { - SmcResult SmcGetResult(const SmcArguments &args) { + SmcResult SmcGetResult(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcGetResultData(const SmcArguments &args) { + SmcResult SmcGetResultData(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere2/program/source/smc/secmon_smc_result.hpp index 9355f22af..853772e50 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.hpp +++ b/exosphere2/program/source/smc/secmon_smc_result.hpp @@ -25,7 +25,7 @@ namespace ams::secmon::smc { void CancelAsyncOperation(u64 async_key); void EndAsyncOperation(); - SmcResult SmcGetResult(const SmcArguments &args); - SmcResult SmcGetResultData(const SmcArguments &args); + SmcResult SmcGetResult(SmcArguments &args); + SmcResult SmcGetResultData(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp index 78c98e620..881a9ea65 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -19,12 +19,12 @@ namespace ams::secmon::smc { - SmcResult SmcModularExponentiate(const SmcArguments &args) { + SmcResult SmcModularExponentiate(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } - SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args) { + SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; } diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp index c2dcd5cbe..cfa4301fe 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.hpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp @@ -19,7 +19,7 @@ namespace ams::secmon::smc { - SmcResult SmcModularExponentiate(const SmcArguments &args); - SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args); + SmcResult SmcModularExponentiate(SmcArguments &args); + SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args); } diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp index 7f309daf1..72947de8e 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp @@ -17,17 +17,186 @@ #include <vapours.hpp> #include <exosphere/reg.hpp> -#define MC_INTSTATUS (0x000) -#define MC_INTMASK (0x004) -#define MC_ERR_STATUS (0x008) -#define MC_ERR_ADR (0x00c) -#define MC_SMMU_CONFIG (0x010) +#define MC_INTSTATUS (0x000) +#define MC_INTMASK (0x004) +#define MC_ERR_STATUS (0x008) +#define MC_ERR_ADR (0x00C) +#define MC_SMMU_CONFIG (0x010) +#define MC_SMMU_PTB_ASID (0x01C) +#define MC_SMMU_PTB_DATA (0x020) +#define MC_SMMU_TLB_FLUSH (0x030) +#define MC_SMMU_PTC_FLUSH (0x034) +#define MC_EMEM_CFG (0x050) +#define MC_EMEM_ADR_CFG (0x054) +#define MC_EMEM_ARB_CFG (0x090) +#define MC_EMEM_ARB_OUTSTANDING_REQ (0x094) +#define MC_EMEM_ARB_TIMING_RCD (0x098) +#define MC_EMEM_ARB_TIMING_RP (0x09C) +#define MC_EMEM_ARB_TIMING_RC (0x0A0) +#define MC_EMEM_ARB_TIMING_RAS (0x0A4) +#define MC_EMEM_ARB_TIMING_FAW (0x0A8) +#define MC_EMEM_ARB_TIMING_RRD (0x0AC) +#define MC_EMEM_ARB_TIMING_RAP2PRE (0x0B0) +#define MC_EMEM_ARB_TIMING_WAP2PRE (0x0B4) +#define MC_EMEM_ARB_TIMING_R2R (0x0B8) +#define MC_EMEM_ARB_TIMING_W2W (0x0BC) +#define MC_EMEM_ARB_TIMING_R2W (0x0C0) +#define MC_EMEM_ARB_TIMING_W2R (0x0C4) +#define MC_EMEM_ARB_MISC2 (0x0C8) +#define MC_EMEM_ARB_DA_TURNS (0x0D0) +#define MC_EMEM_ARB_DA_COVERS (0x0D4) +#define MC_EMEM_ARB_MISC0 (0x0D8) +#define MC_EMEM_ARB_MISC1 (0x0DC) +#define MC_EMEM_ARB_RING1_THROTTLE (0x0E0) +#define MC_CLIENT_HOTRESET_CTRL (0x200) +#define MC_CLIENT_HOTRESET_STATUS (0x204) +#define MC_SMMU_AFI_ASID (0x238) +#define MC_SMMU_DC_ASID (0x240) +#define MC_SMMU_DCB_ASID (0x244) +#define MC_SMMU_HC_ASID (0x250) +#define MC_SMMU_HDA_ASID (0x254) +#define MC_SMMU_ISP2_ASID (0x258) +#define MC_SMMU_NVENC_ASID (0x264) +#define MC_SMMU_NV_ASID (0x268) +#define MC_SMMU_NV2_ASID (0x26C) +#define MC_SMMU_PPCS_ASID (0x270) +#define MC_SMMU_SATA_ASID (0x274) +#define MC_SMMU_VI_ASID (0x280) +#define MC_SMMU_VIC_ASID (0x284) +#define MC_SMMU_XUSB_HOST_ASID (0x288) +#define MC_SMMU_XUSB_DEV_ASID (0x28C) +#define MC_SMMU_TSEC_ASID (0x294) +#define MC_LATENCY_ALLOWANCE_AVPC_0 (0x2E4) +#define MC_LATENCY_ALLOWANCE_DC_0 (0x2E8) +#define MC_LATENCY_ALLOWANCE_DC_1 (0x2EC) +#define MC_LATENCY_ALLOWANCE_DCB_0 (0x2F4) +#define MC_LATENCY_ALLOWANCE_DCB_1 (0x2F8) +#define MC_LATENCY_ALLOWANCE_HC_0 (0x310) +#define MC_LATENCY_ALLOWANCE_HC_1 (0x314) +#define MC_LATENCY_ALLOWANCE_MPCORE_0 (0x320) +#define MC_LATENCY_ALLOWANCE_NVENC_0 (0x328) +#define MC_LATENCY_ALLOWANCE_PPCS_0 (0x344) +#define MC_LATENCY_ALLOWANCE_PPCS_1 (0x348) +#define MC_LATENCY_ALLOWANCE_ISP2_0 (0x370) +#define MC_LATENCY_ALLOWANCE_ISP2_1 (0x374) +#define MC_LATENCY_ALLOWANCE_XUSB_0 (0x37C) +#define MC_LATENCY_ALLOWANCE_XUSB_1 (0x380) +#define MC_LATENCY_ALLOWANCE_TSEC_0 (0x390) +#define MC_LATENCY_ALLOWANCE_VIC_0 (0x394) +#define MC_LATENCY_ALLOWANCE_VI2_0 (0x398) +#define MC_LATENCY_ALLOWANCE_GPU_0 (0x3AC) +#define MC_LATENCY_ALLOWANCE_SDMMCA_0 (0x3B8) +#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 (0x3BC) +#define MC_LATENCY_ALLOWANCE_SDMMC_0 (0x3C0) +#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 (0x3C4) +#define MC_LATENCY_ALLOWANCE_NVDEC_0 (0x3D8) +#define MC_LATENCY_ALLOWANCE_GPU2_0 (0x3E8) +#define MC_DIS_PTSA_RATE (0x41C) +#define MC_DIS_PTSA_MIN (0x420) +#define MC_DIS_PTSA_MAX (0x424) +#define MC_DISB_PTSA_RATE (0x428) +#define MC_DISB_PTSA_MIN (0x42C) +#define MC_DISB_PTSA_MAX (0x430) +#define MC_VE_PTSA_RATE (0x434) +#define MC_VE_PTSA_MIN (0x438) +#define MC_VE_PTSA_MAX (0x43C) +#define MC_MLL_MPCORER_PTSA_RATE (0x44C) +#define MC_RING1_PTSA_RATE (0x47C) +#define MC_RING1_PTSA_MIN (0x480) +#define MC_RING1_PTSA_MAX (0x484) +#define MC_PCX_PTSA_RATE (0x4AC) +#define MC_PCX_PTSA_MIN (0x4B0) +#define MC_PCX_PTSA_MAX (0x4B4) +#define MC_MSE_PTSA_RATE (0x4C4) +#define MC_MSE_PTSA_MIN (0x4C8) +#define MC_MSE_PTSA_MAX (0x4CC) +#define MC_AHB_PTSA_RATE (0x4DC) +#define MC_AHB_PTSA_MIN (0x4E0) +#define MC_AHB_PTSA_MAX (0x4E4) +#define MC_APB_PTSA_RATE (0x4E8) +#define MC_APB_PTSA_MIN (0x4EC) +#define MC_APB_PTSA_MAX (0x4F0) +#define MC_FTOP_PTSA_RATE (0x50C) +#define MC_HOST_PTSA_RATE (0x518) +#define MC_HOST_PTSA_MIN (0x51C) +#define MC_HOST_PTSA_MAX (0x520) +#define MC_USBX_PTSA_RATE (0x524) +#define MC_USBX_PTSA_MIN (0x528) +#define MC_USBX_PTSA_MAX (0x52C) +#define MC_USBD_PTSA_RATE (0x530) +#define MC_USBD_PTSA_MIN (0x534) +#define MC_USBD_PTSA_MAX (0x538) +#define MC_GK_PTSA_RATE (0x53C) +#define MC_GK_PTSA_MIN (0x540) +#define MC_GK_PTSA_MAX (0x544) +#define MC_AUD_PTSA_RATE (0x548) +#define MC_AUD_PTSA_MIN (0x54C) +#define MC_AUD_PTSA_MAX (0x550) +#define MC_VICPC_PTSA_RATE (0x554) +#define MC_VICPC_PTSA_MIN (0x558) +#define MC_VICPC_PTSA_MAX (0x55C) +#define MC_JPG_PTSA_RATE (0x584) +#define MC_JPG_PTSA_MIN (0x588) +#define MC_JPG_PTSA_MAX (0x58C) +#define MC_GK2_PTSA_RATE (0x610) +#define MC_GK2_PTSA_MIN (0x614) +#define MC_GK2_PTSA_MAX (0x618) +#define MC_SDM_PTSA_RATE (0x61C) +#define MC_SDM_PTSA_MIN (0x620) +#define MC_SDM_PTSA_MAX (0x624) +#define MC_HDAPC_PTSA_RATE (0x628) +#define MC_HDAPC_PTSA_MIN (0x62C) +#define MC_HDAPC_PTSA_MAX (0x630) +#define MC_SEC_CARVEOUT_BOM (0x670) +#define MC_SEC_CARVEOUT_SIZE_MB (0x674) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A (0x690) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB (0x694) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B (0x698) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB (0x69C) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C (0x6A0) +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB (0x6A4) +#define MC_EMEM_ARB_TIMING_RFCPB (0x6C0) +#define MC_EMEM_ARB_TIMING_CCDMW (0x6C4) +#define MC_EMEM_ARB_REFPB_HP_CTRL (0x6F0) +#define MC_EMEM_ARB_REFPB_BANK_CTRL (0x6F4) +#define MC_PTSA_GRANT_DECREMENT (0x960) +#define MC_CLIENT_HOTRESET_CTRL_1 (0x970) +#define MC_CLIENT_HOTRESET_STATUS_1 (0x974) +#define MC_SMMU_PTC_FLUSH_1 (0x9B8) +#define MC_SMMU_DC1_ASID (0xA88) +#define MC_SMMU_SDMMC1A_ASID (0xA94) +#define MC_SMMU_SDMMC2A_ASID (0xA98) +#define MC_SMMU_SDMMC3A_ASID (0xA9C) +#define MC_SMMU_SDMMC4A_ASID (0xAA0) +#define MC_SMMU_ISP2B_ASID (0xAA4) +#define MC_SMMU_GPU_ASID (0xAA8) +#define MC_SMMU_GPUB_ASID (0xAAC) +#define MC_SMMU_PPCS2_ASID (0xAB0) +#define MC_SMMU_NVDEC_ASID (0xAB4) +#define MC_SMMU_APE_ASID (0xAB8) +#define MC_SMMU_SE_ASID (0xABC) +#define MC_SMMU_NVJPG_ASID (0xAC0) +#define MC_SMMU_HC1_ASID (0xAC4) +#define MC_SMMU_SE1_ASID (0xAC8) +#define MC_SMMU_AXIAP_ASID (0xACC) +#define MC_SMMU_ETR_ASID (0xAD0) +#define MC_SMMU_TSECB_ASID (0xAD4) +#define MC_SMMU_TSEC1_ASID (0xAD8) +#define MC_SMMU_TSECB1_ASID (0xADC) +#define MC_SMMU_NVDEC1_ASID (0xAE0) +#define MC_EMEM_ARB_DHYST_CTRL (0xBCC) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 (0xBD0) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 (0xBD4) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 (0xBD8) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 (0xBDC) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 (0xBE0) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 (0xBE4) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 (0xBE8) +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 (0xBEC) +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS (0xC00) + #define MC_SMMU_TLB_CONFIG (0x014) #define MC_SMMU_PTC_CONFIG (0x018) -#define MC_SMMU_PTB_ASID (0x01c) -#define MC_SMMU_PTB_DATA (0x020) -#define MC_SMMU_TLB_FLUSH (0x030) -#define MC_SMMU_PTC_FLUSH (0x034) #define MC_SMMU_AVPC_ASID (0x23C) #define MC_SMMU_PPCS1_ASID (0x298) @@ -145,6 +314,33 @@ #define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd78) #define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd7c) +#define MC_STAT_CONTROL (0x100) +#define MC_STAT_EMC_CLOCK_LIMIT (0x108) +#define MC_STAT_EMC_CLOCK_LIMIT_MSBS (0x10c) +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO (0x118) +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI (0x11c) +#define MC_STAT_EMC_FILTER_SET0_SPARE (0x124) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 (0x128) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 (0x12c) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 (0x130) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 (0x134) +#define MC_STAT_EMC_SET0_COUNT (0x138) +#define MC_STAT_EMC_SET0_COUNT_MSBS (0x13c) +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO (0x158) +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI (0x15c) +#define MC_STAT_EMC_FILTER_SET1_SPARE (0x164) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 (0x168) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 (0x16c) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 (0x170) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 (0x174) +#define MC_STAT_EMC_SET1_COUNT (0x178) +#define MC_STAT_EMC_SET1_COUNT_MSBS (0x17c) +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER (0xa20) +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER (0xa24) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 (0xb88) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 (0xb8c) +#define MC_STAT_EMC_FILTER_SET0_CLIENT_5 (0xbc4) +#define MC_STAT_EMC_FILTER_SET1_CLIENT_5 (0xbc8) #define MC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MC, NAME) #define MC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MC, NAME, VALUE) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index 27a809567..3f1d90eda 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -17,72 +17,95 @@ #include <vapours.hpp> #include <exosphere/reg.hpp> -#define APBDEV_PMC_CNTRL (0x000) -#define APBDEV_PMC_DPD_SAMPLE (0x020) -#define APBDEV_PMC_DPD_ENABLE (0x024) -#define APBDEV_PMC_CLAMP_STATUS (0x02C) -#define APBDEV_PMC_PWRGATE_TOGGLE (0x030) -#define APBDEV_PMC_PWRGATE_STATUS (0x038) -#define APBDEV_PMC_SCRATCH0 (0x050) -#define APBDEV_PMC_SCRATCH1 (0x054) -#define APBDEV_PMC_SCRATCH12 (0x080) -#define APBDEV_PMC_SCRATCH13 (0x084) -#define APBDEV_PMC_SCRATCH18 (0x098) -#define APBDEV_PMC_SCRATCH20 (0x0A0) -#define APBDEV_PMC_CRYPTO_OP (0x0F4) -#define APBDEV_PM (0x014) -#define APBDEV_PMC_WAKE2_STATUS (0x168) -#define APBDEV_PMC_WEAK_BIAS (0x2C8) -#define APBDEV_PMC_CNTRL2 (0x440) -#define APBDEV_PMC_FUSE_CTRL (0x450) -#define APBDEV_PMC_IO_DPD3_REQ (0x45C) -#define APBDEV_PMC_IO_DPD3_STATUS (0x460) -#define APBDEV_PMC_IO_DPD4_REQ (0x464) -#define APBDEV_PMC_IO_DPD4_STATUS (0x468) -#define APBDEV_PMC_SET_SW_CLAMP (0x47C) -#define APBDEV_PMC_DDR_CNTRL (0x4E4) -#define APBDEV_PMC_SEC_DISABLE (0x004) -#define APBDEV_PMC_SEC_DISABLE2 (0x2C4) -#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) -#define APBDEV_PMC_SEC_DISABLE4 (0x5B0) -#define APBDEV_PMC_SEC_DISABLE5 (0x5B4) -#define APBDEV_PMC_SEC_DISABLE6 (0x5B8) -#define APBDEV_PMC_SEC_DISABLE7 (0x5BC) -#define APBDEV_PMC_SEC_DISABLE8 (0x5C0) -#define APBDEV_PMC_SCRATCH43 (0x22C) -#define APBDEV_PMC_SCRATCH190 (0x818) -#define APBDEV_PMC_SCRATCH200 (0x840) -#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) -#define APBDEV_PMC_SECURE_SCRATCH4 (0x0C0) -#define APBDEV_PMC_SECURE_SCRATCH5 (0x0C4) -#define APBDEV_PMC_SECURE_SCRATCH6 (0x224) -#define APBDEV_PMC_SECURE_SCRATCH7 (0x228) -#define APBDEV_PMC_SECURE_SCRATCH16 (0x320) -#define APBDEV_PMC_SECURE_SCRATCH21 (0x334) -#define APBDEV_PMC_SECURE_SCRATCH24 (0x340) -#define APBDEV_PMC_SECURE_SCRATCH25 (0x344) -#define APBDEV_PMC_SECURE_SCRATCH26 (0x348) -#define APBDEV_PMC_SECURE_SCRATCH27 (0x34C) -#define APBDEV_PMC_SECURE_SCRATCH32 (0x360) -#define APBDEV_PMC_SECURE_SCRATCH34 (0x368) -#define APBDEV_PMC_SECURE_SCRATCH35 (0x36C) -#define APBDEV_PMC_SECURE_SCRATCH39 (0x37C) -#define APBDEV_PMC_SECURE_SCRATCH51 (0x3AC) -#define APBDEV_PMC_SECURE_SCRATCH55 (0x3BC) -#define APBDEV_PMC_SECURE_SCRATCH74 (0x408) -#define APBDEV_PMC_SECURE_SCRATCH75 (0x40C) -#define APBDEV_PMC_SECURE_SCRATCH76 (0x410) -#define APBDEV_PMC_SECURE_SCRATCH77 (0x414) -#define APBDEV_PMC_SECURE_SCRATCH78 (0x418) -#define APBDEV_PMC_SECURE_SCRATCH99 (0xAE4) -#define APBDEV_PMC_SECURE_SCRATCH100 (0xAE8) -#define APBDEV_PMC_SECURE_SCRATCH101 (0xAEC) -#define APBDEV_PMC_SECURE_SCRATCH102 (0xAF0) -#define APBDEV_PMC_SECURE_SCRATCH103 (0xAF4) -#define APBDEV_PMC_SECURE_SCRATCH112 (0xB18) -#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C) -#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20) -#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24) +#define APBDEV_PMC_CNTRL (0x000) +#define APBDEV_PMC_WAKE_MASK (0x00C) +#define APBDEV_PMC_WAKE_LVL (0x010) +#define APBDEV_PMC_WAKE_STATUS (0x014) +#define APBDEV_PMC_DPD_PADS_ORIDE (0x01C) +#define APBDEV_PMC_DPD_SAMPLE (0x020) +#define APBDEV_PMC_DPD_ENABLE (0x024) +#define APBDEV_PMC_CLAMP_STATUS (0x02C) +#define APBDEV_PMC_PWRGATE_TOGGLE (0x030) +#define APBDEV_PMC_REMOVE_CLAMPING_CMD (0x034) +#define APBDEV_PMC_PWRGATE_STATUS (0x038) +#define APBDEV_PMC_PWRGOOD_TIMER (0x03C) +#define APBDEV_PMC_BLINK_TIMER (0x040) +#define APBDEV_PMC_NO_IOPOWER (0x044) +#define APBDEV_PMC_PWR_DET (0x048) +#define APBDEV_PMC_SCRATCH0 (0x050) +#define APBDEV_PMC_SCRATCH1 (0x054) +#define APBDEV_PMC_SCRATCH12 (0x080) +#define APBDEV_PMC_SCRATCH13 (0x084) +#define APBDEV_PMC_SCRATCH18 (0x098) +#define APBDEV_PMC_SCRATCH20 (0x0A0) +#define APBDEV_PMC_AUTO_WAKE_LVL_MASK (0x0DC) +#define APBDEV_PMC_WAKE_DELAY (0x0E0) +#define APBDEV_PMC_PWR_DET_VAL (0x0E4) +#define APBDEV_PMC_CRYPTO_OP (0x0F4) +#define APBDEV_PMC_WAKE2_MASK (0x160) +#define APBDEV_PMC_WAKE2_LVL (0x164) +#define APBDEV_PMC_WAKE2_STATUS (0x168) +#define APBDEV_PMC_AUTO_WAKE2_LVL_MASK (0x170) +#define APBDEV_PMC_CLK_OUT_CNTRL (0x1A8) +#define APBDEV_PMC_IO_DPD_REQ (0x1B8) +#define APBDEV_PMC_IO_DPD_STATUS (0x1BC) +#define APBDEV_PMC_IO_DPD2_REQ (0x1C0) +#define APBDEV_PMC_IO_DPD2_STATUS (0x1C4) +#define APBDEV_PMC_SEL_DPD_TIM (0x1C8) +#define APBDEV_PMC_TSC_MULT (0x2B4) +#define APBDEV_PMC_WEAK_BIAS (0x2C8) +#define APBDEV_PMC_GPU_RG_CNTRL (0x2D4) +#define APBDEV_PMC_CNTRL2 (0x440) +#define APBDEV_PMC_FUSE_CTRL (0x450) +#define APBDEV_PMC_IO_DPD3_REQ (0x45C) +#define APBDEV_PMC_IO_DPD3_STATUS (0x460) +#define APBDEV_PMC_IO_DPD4_REQ (0x464) +#define APBDEV_PMC_IO_DPD4_STATUS (0x468) +#define APBDEV_PMC_SET_SW_CLAMP (0x47C) +#define APBDEV_PMC_WAKE_DEBOUNCE_EN (0x4D8) +#define APBDEV_PMC_DDR_CNTRL (0x4E4) +#define APBDEV_PMC_SEC_DISABLE (0x004) +#define APBDEV_PMC_SEC_DISABLE2 (0x2C4) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SEC_DISABLE4 (0x5B0) +#define APBDEV_PMC_SEC_DISABLE5 (0x5B4) +#define APBDEV_PMC_SEC_DISABLE6 (0x5B8) +#define APBDEV_PMC_SEC_DISABLE7 (0x5BC) +#define APBDEV_PMC_SEC_DISABLE8 (0x5C0) +#define APBDEV_PMC_SCRATCH43 (0x22C) +#define APBDEV_PMC_SCRATCH190 (0x818) +#define APBDEV_PMC_SCRATCH200 (0x840) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SECURE_SCRATCH4 (0x0C0) +#define APBDEV_PMC_SECURE_SCRATCH5 (0x0C4) +#define APBDEV_PMC_SECURE_SCRATCH6 (0x224) +#define APBDEV_PMC_SECURE_SCRATCH7 (0x228) +#define APBDEV_PMC_SECURE_SCRATCH16 (0x320) +#define APBDEV_PMC_SECURE_SCRATCH21 (0x334) +#define APBDEV_PMC_SECURE_SCRATCH24 (0x340) +#define APBDEV_PMC_SECURE_SCRATCH25 (0x344) +#define APBDEV_PMC_SECURE_SCRATCH26 (0x348) +#define APBDEV_PMC_SECURE_SCRATCH27 (0x34C) +#define APBDEV_PMC_SECURE_SCRATCH32 (0x360) +#define APBDEV_PMC_SECURE_SCRATCH34 (0x368) +#define APBDEV_PMC_SECURE_SCRATCH35 (0x36C) +#define APBDEV_PMC_SECURE_SCRATCH39 (0x37C) +#define APBDEV_PMC_SECURE_SCRATCH51 (0x3AC) +#define APBDEV_PMC_SECURE_SCRATCH55 (0x3BC) +#define APBDEV_PMC_SECURE_SCRATCH74 (0x408) +#define APBDEV_PMC_SECURE_SCRATCH75 (0x40C) +#define APBDEV_PMC_SECURE_SCRATCH76 (0x410) +#define APBDEV_PMC_SECURE_SCRATCH77 (0x414) +#define APBDEV_PMC_SECURE_SCRATCH78 (0x418) +#define APBDEV_PMC_SECURE_SCRATCH99 (0xAE4) +#define APBDEV_PMC_SECURE_SCRATCH100 (0xAE8) +#define APBDEV_PMC_SECURE_SCRATCH101 (0xAEC) +#define APBDEV_PMC_SECURE_SCRATCH102 (0xAF0) +#define APBDEV_PMC_SECURE_SCRATCH103 (0xAF4) +#define APBDEV_PMC_SECURE_SCRATCH112 (0xB18) +#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C) +#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20) +#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24) #define PMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APBDEV_PMC, NAME) From 6bf283ec2ee8fd51f6cb96e3fb4d2bd7acfd19aa Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 02:32:17 -0700 Subject: [PATCH 060/118] exo2: implement SmcGetConfig --- exosphere2/program/source/secmon_error.cpp | 6 +- .../program/source/secmon_exception_vectors.s | 2 +- exosphere2/program/source/secmon_misc.cpp | 11 + exosphere2/program/source/secmon_misc.hpp | 2 + .../program/source/smc/secmon_smc_handler.cpp | 13 +- .../program/source/smc/secmon_smc_info.cpp | 262 +++++++++++++++++- .../program/source/smc/secmon_smc_info.hpp | 2 +- .../source/smc/secmon_smc_register_access.cpp | 18 +- libraries/config/common.mk | 4 +- .../libexosphere/include/exosphere/fuse.hpp | 58 +++- .../pkg1/pkg1_bootloader_parameters.hpp | 4 +- .../secmon/secmon_emummc_context.hpp | 16 ++ .../secmon/secmon_monitor_context.hpp | 2 + .../libexosphere/source/fuse/fuse_api.cpp | 260 +++++++++++++++-- .../source/fuse/fuse_registers.hpp | 25 ++ 15 files changed, 640 insertions(+), 45 deletions(-) diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp index a9e6181c5..c05bf4cd7 100644 --- a/exosphere2/program/source/secmon_error.cpp +++ b/exosphere2/program/source/secmon_error.cpp @@ -36,6 +36,8 @@ namespace ams::diag { } *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); } #endif @@ -67,8 +69,8 @@ namespace ams::secmon { *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x0C) = static_cast<u32>(temp_reg >> 32); __asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory"); - *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast<u32>(temp_reg >> 0); - *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x1C) = static_cast<u32>(temp_reg >> 32); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(temp_reg >> 0); + *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast<u32>(temp_reg >> 32); __asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory"); *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast<u32>(temp_reg >> 0); diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere2/program/source/secmon_exception_vectors.s index ef1ab9f32..2749f9f64 100644 --- a/exosphere2/program/source/secmon_exception_vectors.s +++ b/exosphere2/program/source/secmon_exception_vectors.s @@ -211,7 +211,7 @@ _ZN3ams6secmon25HandleSmcExceptionCore012Ev: /* Restore our core-specific stack. */ ldp x29, x30, [sp], #0x10 - mov x30, sp + mov sp, x30 /* Release our exclusive access to the common smc stack. */ stp x0, x1, [sp, #-0x10]! diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp index d0981d771..eed73b5eb 100644 --- a/exosphere2/program/source/secmon_misc.cpp +++ b/exosphere2/program/source/secmon_misc.cpp @@ -23,11 +23,18 @@ namespace ams::secmon { constinit pkg1::BctParameters g_bct_params = {}; constinit se::Sha256Hash g_package2_hash = {}; + constinit u32 g_deprecated_boot_reason_value = {}; + constinit u8 g_deprecated_boot_reason_state = {}; + } void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params) { /* Save the BCT parameters. */ g_bct_params = secmon_params.bct_params; + + /* Save the deprecated boot reason. */ + g_deprecated_boot_reason_value = secmon_params.deprecated_boot_reason_value; + g_deprecated_boot_reason_state = secmon_params.deprecated_boot_reason_state; } bool IsRecoveryBoot() { @@ -52,4 +59,8 @@ namespace ams::secmon { g_package2_hash = hash; } + u32 GetDeprecatedBootReason() { + return (static_cast<u32>(g_deprecated_boot_reason_state) << 24) | (g_deprecated_boot_reason_value & 0x00FFFFFF); + } + } diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp index e8b5922ff..f637d0a64 100644 --- a/exosphere2/program/source/secmon_misc.hpp +++ b/exosphere2/program/source/secmon_misc.hpp @@ -29,4 +29,6 @@ namespace ams::secmon { void GetPackage2Hash(se::Sha256Hash *out); void SetPackage2Hash(const se::Sha256Hash &hash); + u32 GetDeprecatedBootReason(); + } \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 918824504..ac960fae4 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -128,7 +128,7 @@ namespace ams::secmon::smc { { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu }, { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu }, - { 0xC3000002, Restriction_Normal, SmcGetConfigKern }, + { 0xC3000004, Restriction_Normal, SmcGetConfigKern }, { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking }, { 0xC3000006, Restriction_Normal, SmcShowError }, { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, @@ -240,6 +240,17 @@ namespace ams::secmon::smc { *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + util::WaitMicroSeconds(1000); + } + if (args.r[0] != static_cast<u64>(SmcResult::Success)) { + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC; + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id); + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; + } + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + util::WaitMicroSeconds(1000); } #endif diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index fba5f75ad..08540e09e 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -15,18 +15,272 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_misc.hpp" #include "secmon_smc_info.hpp" +#include "secmon_smc_power_management.hpp" namespace ams::secmon::smc { + namespace { + + struct KernelConfiguration { + /* Secure Monitor view. */ + using Flags1 = util::BitPack32::Field< 0, 8>; + using Flags0 = util::BitPack32::Field< 8, 8>; + using PhysicalMemorySize = util::BitPack32::Field<16, 2>; + + /* Kernel view, from libmesosphere. */ + using DebugFillMemory = util::BitPack32::Field<0, 1, bool>; + using EnableUserExceptionHandlers = util::BitPack32::Field<DebugFillMemory::Next, 1, bool>; + using EnableUserPmuAccess = util::BitPack32::Field<EnableUserExceptionHandlers::Next, 1, bool>; + using IncreaseThreadResourceLimit = util::BitPack32::Field<EnableUserPmuAccess::Next, 1, bool>; + using Reserved4 = util::BitPack32::Field<IncreaseThreadResourceLimit::Next, 4, u32>; + using UseSecureMonitorPanicCall = util::BitPack32::Field<Reserved4::Next, 1, bool>; + using Reserved9 = util::BitPack32::Field<UseSecureMonitorPanicCall::Next, 7, u32>; + using MemorySize = util::BitPack32::Field<Reserved9::Next, 2, u32>; /* smc::MemorySize = pkg1::MemorySize */ + }; + + constexpr const pkg1::MemorySize DramIdToMemorySize[fuse::DramId_Count] = { + [fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_CopperSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB, + [fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung8GB] = pkg1::MemorySize_8GB, + [fuse::DramId_IowaHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagSamsung8GB] = pkg1::MemorySize_8GB, + [fuse::DramId_HoagHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y4GBX] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y8GBX] = pkg1::MemorySize_8GB, + [fuse::DramId_HoagSamsung1y4GBX] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y4GBY] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y8GBY] = pkg1::MemorySize_8GB, + [fuse::DramId_IowaSamsung1y4GBA] = pkg1::MemorySize_4GB, + [fuse::DramId_FiveSamsung1y8GBX] = pkg1::MemorySize_8GB, + [fuse::DramId_FiveSamsung1y4GBX] = pkg1::MemorySize_4GB, + }; + + constexpr const pkg1::MemoryMode MemoryModes[] = { + pkg1::MemoryMode_Auto, + + pkg1::MemoryMode_4GB, + pkg1::MemoryMode_4GBAppletDev, + pkg1::MemoryMode_4GBSystemDev, + + pkg1::MemoryMode_6GB, + pkg1::MemoryMode_6GBAppletDev, + + pkg1::MemoryMode_8GB, + }; + + constexpr bool IsValidMemoryMode(pkg1::MemoryMode mode) { + for (const auto known_mode : MemoryModes) { + if (mode == known_mode) { + return true; + } + } + return false; + } + + pkg1::MemoryMode SanitizeMemoryMode(pkg1::MemoryMode mode) { + if (IsValidMemoryMode(mode)) { + return mode; + } + return pkg1::MemoryMode_Auto; + } + + pkg1::MemorySize GetPhysicalMemorySize() { + const auto dram_id = fuse::GetDramId(); + AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); + return DramIdToMemorySize[dram_id]; + } + + pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) { + return std::min(GetPhysicalMemorySize(), size); + } + + pkg1::MemoryMode GetMemoryMode(pkg1::MemoryMode mode) { + /* Sanitize the mode. */ + mode = SanitizeMemoryMode(mode); + + /* If the mode is auto, construct the memory mode. */ + if (mode == pkg1::MemoryMode_Auto) { + return pkg1::MakeMemoryMode(GetPhysicalMemorySize(), pkg1::MemoryArrange_Normal); + } else { + const auto mode_size = GetMemorySize(mode); + const auto mode_arrange = GetMemoryArrange(mode); + const auto size = GetAvailableMemorySize(mode_size); + const auto arrange = (size == mode_size) ? mode_arrange : pkg1::MemoryArrange_Normal; + return pkg1::MakeMemoryMode(size, arrange); + } + } + + u32 GetMemoryMode() { + /* Unless development function is enabled, we're 4 GB. */ + u32 memory_mode = pkg1::MemoryMode_4GB; + + if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { + memory_mode = GetMemoryMode(bcd.GetMemoryMode()); + } + + return memory_mode; + } + + u32 GetKernelConfiguration() { + pkg1::MemorySize memory_size = pkg1::MemorySize_4GB; + util::BitPack32 value = {}; + + if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { + memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode())); + + value.Set<KernelConfiguration::Flags1>(bcd.GetKernelFlags1()); + value.Set<KernelConfiguration::Flags0>(bcd.GetKernelFlags0()); + } + + value.Set<KernelConfiguration::PhysicalMemorySize>(memory_size); + + /* Exosphere extensions. */ + const auto &sc = GetSecmonConfiguration(); + + if (!sc.DisableUserModeExceptionHandlers()) { + value.Set<KernelConfiguration::EnableUserExceptionHandlers>(true); + } + + if (sc.EnableUserModePerformanceCounterAccess()) { + value.Set<KernelConfiguration::EnableUserPmuAccess>(true); + } + + return value.value; + } + + SmcResult GetConfig(SmcArguments &args, bool kern) { + switch (static_cast<ConfigItem>(args.r[1])) { + case ConfigItem::DisableProgramVerification: + args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled(); + break; + case ConfigItem::DramId: + args.r[1] = fuse::GetDramId(); + break; + case ConfigItem::SecurityEngineInterruptNumber: + args.r[1] = SecurityEngineUserInterruptId; + break; + case ConfigItem::FuseVersion: + args.r[1] = fuse::GetExpectedFuseVersion(GetTargetFirmware()); + break; + case ConfigItem::HardwareType: + args.r[1] = fuse::GetHardwareType(); + break; + case ConfigItem::HardwareState: + args.r[1] = fuse::GetHardwareState(); + break; + case ConfigItem::IsRecoveryBoot: + args.r[1] = IsRecoveryBoot(); + break; + case ConfigItem::DeviceId: + args.r[1] = fuse::GetDeviceId(); + break; + case ConfigItem::BootReason: + { + /* This was removed in firmware 4.0.0. */ + if (GetTargetFirmware() >= TargetFirmware_4_0_0) { + return SmcResult::InvalidArgument; + } + + args.r[1] = GetDeprecatedBootReason(); + } + break; + case ConfigItem::MemoryMode: + args.r[1] = GetMemoryMode(); + break; + case ConfigItem::IsDevelopmentFunctionEnabled: + args.r[1] = GetSecmonConfiguration().IsDevelopmentFunctionEnabled(kern) || GetBootConfig().data.IsDevelopmentFunctionEnabled(); + break; + case ConfigItem::KernelConfiguration: + args.r[1] = GetKernelConfiguration(); + break; + case ConfigItem::IsChargerHiZModeEnabled: + args.r[1] = IsChargerHiZModeEnabled(); + break; + case ConfigItem::QuestState: + args.r[1] = fuse::GetQuestState(); + break; + case ConfigItem::RegulatorType: + args.r[1] = fuse::GetRegulator(); + break; + case ConfigItem::DeviceUniqueKeyGeneration: + args.r[1] = fuse::GetDeviceUniqueKeyGeneration(); + break; + case ConfigItem::Package2Hash: + { + /* Only allow getting the package2 hash in recovery boot. */ + if (!IsRecoveryBoot()) { + return SmcResult::InvalidArgument; + } + + /* Get the hash. */ + se::Sha256Hash tmp_hash; + GetPackage2Hash(std::addressof(tmp_hash)); + + /* Copy it out. */ + static_assert(sizeof(args) - sizeof(args.r[0]) >= sizeof(tmp_hash)); + std::memcpy(std::addressof(args.r[1]), std::addressof(tmp_hash), sizeof(tmp_hash)); + } + break; + case ConfigItem::ExosphereApiVersion: + /* Get information about the current exosphere version. */ + args.r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) | + (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) | + (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) | + (static_cast<u64>(GetKeyGeneration()) << 32) | + (static_cast<u64>(GetTargetFirmware()) << 00); + break; + case ConfigItem::ExosphereNeedsReboot: + /* We are executing, so we aren't in the process of rebooting. */ + args.r[1] = 0; + break; + case ConfigItem::ExosphereNeedsShutdown: + /* We are executing, so we aren't in the process of shutting down. */ + args.r[1] = 0; + break; + case ConfigItem::ExosphereGitCommitHash: + /* Get information about the current exosphere git commit hash. */ + args.r[1] = ATMOSPHERE_GIT_HASH; + break; + case ConfigItem::ExosphereHasRcmBugPatch: + /* Get information about whether this unit has the RCM bug patched. */ + args.r[1] = fuse::HasRcmVulnerabilityPatch(); + break; + case ConfigItem::ExosphereBlankProdInfo: + /* Get whether this unit should simulate a "blanked" PRODINFO. */ + args.r[1] = GetSecmonConfiguration().ShouldUseBlankCalibrationBinary(); + break; + case ConfigItem::ExosphereAllowCalWrites: + /* Get whether this unit should allow writing to the calibration partition. */ + args.r[1] = (GetEmummcConfiguration().IsEmummcActive() || GetSecmonConfiguration().AllowWritingToCalibrationBinarySysmmc()); + break; + default: + return SmcResult::InvalidArgument; + } + + return SmcResult::Success; + } + + } + SmcResult SmcGetConfigUser(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return GetConfig(args, false); } SmcResult SmcGetConfigKern(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return GetConfig(args, true); } SmcResult SmcSetConfig(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp index 907682b8a..26d28e17b 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -34,7 +34,7 @@ namespace ams::secmon::smc { IsDevelopmentFunctionEnabled = 11, KernelConfiguration = 12, IsChargerHiZModeEnabled = 13, - IsQuest = 14, + QuestState = 14, RegulatorType = 15, DeviceUniqueKeyGeneration = 16, Package2Hash = 17, diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp index 0f3e6e08e..b9e8b4d63 100644 --- a/exosphere2/program/source/smc/secmon_smc_register_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -24,9 +24,7 @@ namespace ams::secmon::smc { template<size_t N> constexpr void SetRegisterTableAllowed(std::array<u8, N> &arr, uintptr_t reg) { /* All registers should be four byte aligned. */ - if (reg % sizeof(u32) != 0) { - __builtin_unreachable(); - } + AMS_ASSUME(reg % sizeof(u32) == 0); /* Reduce the register to an index. */ reg /= sizeof(u32); @@ -36,24 +34,18 @@ namespace ams::secmon::smc { const auto mask = (1u << (reg % BITSIZEOF(u8))); /* Check that the permission bit isn't already set. */ - if ((arr[index] & mask) != 0) { - __builtin_unreachable(); - } + AMS_ASSUME((arr[index] & mask) == 0); /* Set the permission bit. */ arr[index] |= mask; /* Ensure that indices are set in sorted order. */ for (auto i = (reg % BITSIZEOF(u8)) + 1; i < 8; ++i) { - if ((arr[index] & (1u << i)) != 0) { - __builtin_unreachable(); - } + AMS_ASSUME((arr[index] & (1u << i)) == 0); } for (auto i = index + 1; i < arr.size(); ++i) { - if (arr[i] != 0) { - __builtin_unreachable(); - } + AMS_ASSUME(arr[i] == 0); } } @@ -72,7 +64,7 @@ namespace ams::secmon::smc { } /* All empty perm table is disallowed. */ - __builtin_unreachable(); + AMS_ASSUME(false); } diff --git a/libraries/config/common.mk b/libraries/config/common.mk index e3a12dd7b..ebe9bcfb8 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -80,7 +80,9 @@ else export ATMOSPHERE_GIT_REVISION := $(ATMOSPHERE_GIT_BRANCH)-$(shell git rev-parse --short HEAD)-dirty endif -ATMOSPHERE_DEFINES += -DATMOSPHERE_GIT_BRANCH=\"$(ATMOSPHERE_GIT_BRANCH)\" -DATMOSPHERE_GIT_REVISION=\"$(ATMOSPHERE_GIT_REVISION)\" +export ATMOSPHERE_GIT_HASH := $(shell git rev-parse --short=16 HEAD) + +ATMOSPHERE_DEFINES += -DATMOSPHERE_GIT_BRANCH=\"$(ATMOSPHERE_GIT_BRANCH)\" -DATMOSPHERE_GIT_REVISION=\"$(ATMOSPHERE_GIT_REVISION)\" -DATMOSPHERE_GIT_HASH="0x$(ATMOSPHERE_GIT_HASH)" #--------------------------------------------------------------------------------- # Ensure top directory is set. diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index 55efdb334..e6ff9ebb9 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -31,21 +31,77 @@ namespace ams::fuse { HardwareType_Undefined = 0xF, }; + enum SocType { + SocType_Erista = 0, + SocType_Mariko = 1, + SocType_Undefined = 0xF, + }; + enum HardwareState { HardwareState_Development = 0, HardwareState_Production = 1, HardwareState_Undefined = 2, }; + enum DramId { + DramId_IcosaSamsung4GB = 0, + DramId_IcosaHynix4GB = 1, + DramId_IcosaMicron4GB = 2, + DramId_CopperSamsung4GB = 3, + DramId_IcosaSamsung6GB = 4, + DramId_CopperHynix4GB = 5, + DramId_CopperMicron4GB = 6, + DramId_IowaX1X2Samsung4GB = 7, + DramId_IowaSansung4GB = 8, + DramId_IowaSamsung8GB = 9, + DramId_IowaHynix4GB = 10, + DramId_IowaMicron4GB = 11, + DramId_HoagSamsung4GB = 12, + DramId_HoagSamsung8GB = 13, + DramId_HoagHynix4GB = 14, + DramId_HoagMicron4GB = 15, + DramId_IowaSamsung4GBY = 16, + DramId_IowaSamsung1y4GBX = 17, + DramId_IowaSamsung1y8GBX = 18, + DramId_HoagSamsung1y4GBX = 19, + DramId_IowaSamsung1y4GBY = 20, + DramId_IowaSamsung1y8GBY = 21, + DramId_IowaSamsung1y4GBA = 22, + DramId_FiveSamsung1y8GBX = 23, + DramId_FiveSamsung1y4GBX = 24, + + DramId_Count, + }; + + enum QuestState { + QuestState_Disabled = 0, + QuestState_Enabled = 1, + }; + void SetRegisterAddress(uintptr_t address); void SetWriteSecureOnly(); void Lockout(); + void Activate(); + void Deactivate(); + void Reload(); + + u32 ReadWord(int address); + u32 GetOdmWord(int index); + DramId GetDramId(); + + void GetEcid(br::BootEcid *out); HardwareType GetHardwareType(); HardwareState GetHardwareState(); + u64 GetDeviceId(); + QuestState GetQuestState(); pmic::Regulator GetRegulator(); - void GetEcid(br::BootEcid *out); + int GetDeviceUniqueKeyGeneration(); + + SocType GetSocType(); + int GetExpectedFuseVersion(TargetFirmware target_fw); + bool HasRcmVulnerabilityPatch(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp index 93f70df18..86eda5279 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp @@ -45,7 +45,9 @@ namespace ams::pkg1 { u32 secmon_start_time; u32 secmon_end_time; BctParameters bct_params; - u8 reserved[0xD8]; + u32 deprecated_boot_reason_value; + u8 deprecated_boot_reason_state; + u8 reserved[0xD3]; u32 bootloader_state; u32 secmon_state; u8 reserved2[0x100]; diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp index 044420e8c..b9c8c47db 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp @@ -45,6 +45,14 @@ namespace ams::secmon { EmummcType type; u32 id; u32 fs_version; + + constexpr bool IsValid() const { + return this->magic == Magic; + } + + constexpr bool IsEmummcActive() const { + return this->IsValid() && this->type != EmummcType_None; + } }; static_assert(util::is_pod<EmummcBaseConfiguration>::value); static_assert(sizeof(EmummcBaseConfiguration) == 0x10); @@ -66,6 +74,14 @@ namespace ams::secmon { EmummcFileConfiguration file_cfg; }; EmummcFilePath emu_dir_path; + + constexpr bool IsValid() const { + return this->base_cfg.IsValid(); + } + + constexpr bool IsEmummcActive() const { + return this->base_cfg.IsEmummcActive(); + } }; static_assert(util::is_pod<EmummcConfiguration>::value); static_assert(sizeof(EmummcConfiguration) <= 0x200); diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 1aa0b679d..2b3822b00 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -65,6 +65,8 @@ namespace ams::secmon { constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + + constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); } }; static_assert(util::is_pod<SecureMonitorConfiguration>::value); static_assert(sizeof(SecureMonitorConfiguration) == 0x80); diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index 32d491393..c01286bfe 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -20,6 +20,11 @@ namespace ams::fuse { namespace { + struct OdmWord2 { + using DeviceUniqueKeyGeneration = util::BitPack32::Field<0, 5, int>; + using Reserved = util::BitPack32::Field<5, 27, int>; + }; + struct OdmWord4 { using HardwareState1 = util::BitPack32::Field<0, 2, int>; using HardwareType1 = util::BitPack32::Field<HardwareState1::Next, 1, int>; @@ -52,6 +57,9 @@ namespace ams::fuse { constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFuses.GetAddress(); + constinit bool g_checked_for_rcm_bug_patch = false; + constinit bool g_has_rcm_bug_patch = false; + ALWAYS_INLINE volatile FuseRegisterRegion *GetRegisterRegion() { return reinterpret_cast<volatile FuseRegisterRegion *>(g_register_address); } @@ -64,6 +72,92 @@ namespace ams::fuse { return GetRegisterRegion()->chip; } + bool IsIdle() { + return reg::HasValue(GetRegisters().FUSE_FUSECTRL, FUSE_REG_BITS_ENUM(FUSECTRL_STATE, IDLE)); + } + + void WaitForIdle() { + while (!IsIdle()) { /* ... */ } + } + + bool IsNewFuseFormat() { + /* On mariko, this should always be true. */ + if (GetSocType() != SocType_Erista) { + return true; + } + + /* Require that the format version be non-zero in odm4. */ + if (util::BitPack32{GetOdmWord(4)}.Get<OdmWord4::FormatVersion>() == 0) { + return false; + } + + /* Check that odm word 0/1 are fused with the magic values. */ + constexpr u32 NewFuseFormatMagic0 = 0x8E61ECAE; + constexpr u32 NewFuseFormatMagic1 = 0xF2BA3BB2; + + const u32 w0 = GetOdmWord(0); + const u32 w1 = GetOdmWord(1); + + return w0 == NewFuseFormatMagic0 && w1 == NewFuseFormatMagic1; + } + + constexpr u32 CompressLotCode(u32 lot0) { + constexpr int Radix = 36; + constexpr int Count = 5; + constexpr int Width = 6; + constexpr u32 Mask = (1u << Width) - 1; + + u32 compressed = 0; + + for (int i = Count - 1; i >= 0; --i) { + compressed *= Radix; + compressed += (lot0 >> (i * Width)) & Mask; + } + + return compressed; + } + + constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = { + TargetFirmware_10_0_0, + TargetFirmware_9_1_0, + TargetFirmware_9_0_0, + TargetFirmware_8_1_0, + TargetFirmware_7_0_0, + TargetFirmware_6_2_0, + TargetFirmware_6_0_0, + TargetFirmware_5_0_0, + TargetFirmware_4_0_0, + TargetFirmware_3_0_2, + TargetFirmware_3_0_0, + TargetFirmware_2_0_0, + TargetFirmware_1_0_0, + }; + + constexpr inline int NumFuseIncrements = util::size(FuseVersionIncrementFirmwares); + + /* Verify that the fuse version increment list is sorted. */ + static_assert([] { + for (size_t i = 0; i < util::size(FuseVersionIncrementFirmwares) - 1; ++i) { + if (FuseVersionIncrementFirmwares[i] <= FuseVersionIncrementFirmwares[i + 1]) { + return false; + } + } + return true; + }()); + + constexpr int GetExpectedFuseVersionImpl(TargetFirmware target_fw) { + for (int i = 0; i < NumFuseIncrements; ++i) { + if (target_fw >= FuseVersionIncrementFirmwares[i]) { + return NumFuseIncrements - i; + } + } + return 0; + } + + static_assert(GetExpectedFuseVersionImpl(TargetFirmware_10_0_0) == 13); + static_assert(GetExpectedFuseVersionImpl(TargetFirmware_1_0_0) == 1); + static_assert(GetExpectedFuseVersionImpl(static_cast<TargetFirmware>(0)) == 0); + } void SetRegisterAddress(uintptr_t address) { @@ -78,10 +172,84 @@ namespace ams::fuse { reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE)); } + u32 ReadWord(int address) { + /* Require that the fuse array be idle. */ + AMS_ABORT_UNLESS(IsIdle()); + + /* Get the registers. */ + volatile auto &FUSE = GetRegisters(); + + /* Write the address to read. */ + reg::Write(FUSE.FUSE_FUSEADDR, address); + + /* Set control to read. */ + reg::ReadWrite(FUSE.FUSE_FUSECTRL, FUSE_REG_BITS_ENUM(FUSECTRL_CMD, READ)); + + /* Wait 1 us. */ + util::WaitMicroSeconds(1); + + /* Wait for the array to be idle. */ + WaitForIdle(); + + return reg::Read(FUSE.FUSE_FUSERDATA); + } + u32 GetOdmWord(int index) { return GetChipRegisters().FUSE_RESERVED_ODM[index]; } + void GetEcid(br::BootEcid *out) { + /* Get the registers. */ + const volatile auto &chip = GetChipRegisters(); + + /* Read the ecid components. */ + const u32 vendor = reg::Read(chip.FUSE_OPT_VENDOR_CODE) & ((1u << 4) - 1); + const u32 fab = reg::Read(chip.FUSE_OPT_FAB_CODE) & ((1u << 6) - 1); + const u32 lot0 = reg::Read(chip.FUSE_OPT_LOT_CODE_0) /* all 32 bits */ ; + const u32 lot1 = reg::Read(chip.FUSE_OPT_LOT_CODE_1) & ((1u << 28) - 1); + const u32 wafer = reg::Read(chip.FUSE_OPT_WAFER_ID) & ((1u << 6) - 1); + const u32 x_coord = reg::Read(chip.FUSE_OPT_X_COORDINATE) & ((1u << 9) - 1); + const u32 y_coord = reg::Read(chip.FUSE_OPT_Y_COORDINATE) & ((1u << 9) - 1); + const u32 reserved = reg::Read(chip.FUSE_OPT_OPS_RESERVED) & ((1u << 6) - 1); + + /* Clear the output. */ + util::ClearMemory(out, sizeof(*out)); + + /* Copy the component bits. */ + out->ecid[0] = static_cast<u32>((lot1 << 30) | (wafer << 24) | (x_coord << 15) | (y_coord << 6) | (reserved)); + out->ecid[1] = static_cast<u32>((lot0 << 26) | (lot1 >> 2)); + out->ecid[2] = static_cast<u32>((fab << 26) | (lot0 >> 6)); + out->ecid[3] = static_cast<u32>(vendor); + } + + u64 GetDeviceId() { + /* Get the registers. */ + const volatile auto &chip = GetChipRegisters(); + + /* Read the device id components. */ + /* NOTE: Device ID is "basically" just an alternate encoding of Ecid. */ + /* It elides lot1 (and compresses lot0), but this is fine because */ + /* lot1 is fixed-value for all fused devices. */ + const u64 fab = reg::Read(chip.FUSE_OPT_FAB_CODE) & ((1u << 6) - 1); + const u32 lot0 = reg::Read(chip.FUSE_OPT_LOT_CODE_0) /* all 32 bits */ ; + const u64 wafer = reg::Read(chip.FUSE_OPT_WAFER_ID) & ((1u << 6) - 1); + const u64 x_coord = reg::Read(chip.FUSE_OPT_X_COORDINATE) & ((1u << 9) - 1); + const u64 y_coord = reg::Read(chip.FUSE_OPT_Y_COORDINATE) & ((1u << 9) - 1); + + /* Compress lot0 down from 32-bits to 26. */ + const u64 clot0 = CompressLotCode(lot0) & ((1u << 26) - 1); + + return (y_coord << 0) | + (x_coord << 9) | + (wafer << 18) | + (clot0 << 24) | + (fab << 50); + } + + DramId GetDramId() { + return static_cast<DramId>(util::BitPack32{GetOdmWord(4)}.Get<OdmWord4::DramId>()); + } + HardwareType GetHardwareType() { /* Read the odm word. */ const util::BitPack32 odm_word4 = { GetOdmWord(4) }; @@ -113,33 +281,85 @@ namespace ams::fuse { } } + QuestState GetQuestState() { + return static_cast<QuestState>(util::BitPack32{GetOdmWord(4)}.Get<OdmWord4::QuestState>()); + } + pmic::Regulator GetRegulator() { - /* TODO: How should mariko be handled? This reads from ODM word 28 in fuses (not presesnt in erista...). */ + /* TODO: How should mariko be handled? This reads from ODM word 28 in fuses (not present in erista...). */ return pmic::Regulator_Erista_Max77621; } - void GetEcid(br::BootEcid *out) { - /* Get the registers. */ - const volatile auto &chip = GetChipRegisters(); + int GetDeviceUniqueKeyGeneration() { + if (IsNewFuseFormat()) { + return util::BitPack32{GetOdmWord(2)}.Get<OdmWord2::DeviceUniqueKeyGeneration>(); + } else { + return 0; + } + } - /* Read the ecid components. */ - const u32 vendor = reg::Read(chip.FUSE_OPT_VENDOR_CODE); - const u32 fab = reg::Read(chip.FUSE_OPT_FAB_CODE); - const u32 lot0 = reg::Read(chip.FUSE_OPT_LOT_CODE_0); - const u32 lot1 = reg::Read(chip.FUSE_OPT_LOT_CODE_1); - const u32 wafer = reg::Read(chip.FUSE_OPT_WAFER_ID); - const u32 x_coord = reg::Read(chip.FUSE_OPT_X_COORDINATE); - const u32 y_coord = reg::Read(chip.FUSE_OPT_Y_COORDINATE); - const u32 reserved = reg::Read(chip.FUSE_OPT_OPS_RESERVED); + SocType GetSocType() { + switch (GetHardwareType()) { + case HardwareType_Icosa: + case HardwareType_Copper: + return SocType_Erista; + case HardwareType_Iowa: + case HardwareType_Hoag: + case HardwareType_Calcio: + case HardwareType_Five: + return SocType_Mariko; + default: + return SocType_Undefined; + } + } - /* Clear the output. */ - util::ClearMemory(out, sizeof(*out)); + int GetExpectedFuseVersion(TargetFirmware target_fw) { + return GetExpectedFuseVersionImpl(target_fw); + } - /* Copy the component bits. */ - out->ecid[0] = static_cast<u32>((lot1 << 30) | (wafer << 24) | (x_coord << 15) | (y_coord << 6) | (reserved)); - out->ecid[1] = static_cast<u32>((lot0 << 26) | (lot1 >> 2)); - out->ecid[2] = static_cast<u32>((fab << 26) | (lot0 >> 6)); - out->ecid[3] = static_cast<u32>(vendor); + bool HasRcmVulnerabilityPatch() { + /* Only check for RCM bug patch once, and cache our result. */ + if (!g_checked_for_rcm_bug_patch) { + do { + /* Mariko units are necessarily patched. */ + if (fuse::GetSocType() != SocType_Erista) { + g_has_rcm_bug_patch = true; + break; + } + + /* Some patched units use XUSB in RCM. */ + if (reg::Read(GetChipRegisters().FUSE_RESERVED_SW) & 0x80) { + g_has_rcm_bug_patch = true; + break; + } + + /* Other units have a proper ipatch instead. */ + u32 word_count = reg::Read(GetChipRegisters().FUSE_FIRST_BOOTROM_PATCH_SIZE) & 0x7F; + u32 word_addr = 191; + + while (word_count && !g_has_rcm_bug_patch) { + u32 word0 = ReadWord(word_addr); + u32 ipatch_count = (word0 >> 16) & 0xF; + + for (u32 i = 0; i < ipatch_count && !g_has_rcm_bug_patch; ++i) { + u32 word = ReadWord(word_addr - (i + 1)); + u32 addr = (word >> 16) * 2; + + if (addr == 0x769a) { + g_has_rcm_bug_patch = true; + break; + } + } + + word_addr -= word_count; + word_count = word0 >> 25; + } + } while (0); + + g_checked_for_rcm_bug_patch = true; + } + + return g_has_rcm_bug_patch; } } diff --git a/libraries/libexosphere/source/fuse/fuse_registers.hpp b/libraries/libexosphere/source/fuse/fuse_registers.hpp index 012ef2a87..ff0fcc571 100644 --- a/libraries/libexosphere/source/fuse/fuse_registers.hpp +++ b/libraries/libexosphere/source/fuse/fuse_registers.hpp @@ -213,6 +213,31 @@ namespace ams::fuse { #define DEFINE_FUSE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) #define DEFINE_FUSE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + DEFINE_FUSE_REG_TWO_BIT_ENUM(FUSECTRL_CMD, 0, IDLE, READ, WRITE, SENSE_CTRL); + + DEFINE_FUSE_REG(FUSECTRL_STATE, 16, 5); + + enum FUSE_FUSECTRL_STATE { + FUSE_FUSECTRL_STATE_RESET = 0, + FUSE_FUSECTRL_STATE_POST_RESET = 1, + FUSE_FUSECTRL_STATE_LOAD_ROW0 = 2, + FUSE_FUSECTRL_STATE_LOAD_ROW1 = 3, + FUSE_FUSECTRL_STATE_IDLE = 4, + FUSE_FUSECTRL_STATE_READ_SETUP = 5, + FUSE_FUSECTRL_STATE_READ_STROBE = 6, + FUSE_FUSECTRL_STATE_SAMPLE_FUSES = 7, + FUSE_FUSECTRL_STATE_READ_HOLD = 8, + FUSE_FUSECTRL_STATE_FUSE_SRC_SETUP = 9, + FUSE_FUSECTRL_STATE_WRITE_SETUP = 10, + FUSE_FUSECTRL_STATE_WRITE_ADDR_SETUP = 11, + FUSE_FUSECTRL_STATE_WRITE_PROGRAM = 12, + FUSE_FUSECTRL_STATE_WRITE_ADDR_HOLD = 13, + FUSE_FUSECTRL_STATE_FUSE_SRC_HOLD = 14, + FUSE_FUSECTRL_STATE_LOAD_RIR = 15, + FUSE_FUSECTRL_STATE_READ_BEFORE_WRITE_SETUP = 16, + FUSE_FUSECTRL_STATE_READ_DEASSERT_PD = 17, + }; + DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, 4, KEY_VISIBLE, KEY_INVISIBLE); DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_PRIVATEKEYDISABLE_VAL_KEY, 0, VISIBLE, INVISIBLE); From fa64bf49513ba783475ecdabc5e01a0160403337 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 03:23:31 -0700 Subject: [PATCH 061/118] exo2: implement GenerateRandomBytes --- .../source/smc/secmon_random_cache.cpp | 2 - .../source/smc/secmon_random_cache.hpp | 2 + .../program/source/smc/secmon_smc_common.hpp | 2 +- .../program/source/smc/secmon_smc_random.cpp | 60 +++++++++++++++++-- .../program/source/smc/secmon_smc_se_lock.cpp | 37 ++++++++++++ .../program/source/smc/secmon_smc_se_lock.hpp | 5 ++ 6 files changed, 101 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere2/program/source/smc/secmon_random_cache.cpp index 13b1fd941..f14330b93 100644 --- a/exosphere2/program/source/smc/secmon_random_cache.cpp +++ b/exosphere2/program/source/smc/secmon_random_cache.cpp @@ -21,8 +21,6 @@ namespace ams::secmon::smc { namespace { - constexpr inline size_t MaxRandomBytes = sizeof(SmcArguments) - sizeof(SmcArguments{}.r[0]); - constinit int g_random_offset_low = 0; constinit int g_random_offset_high = 0; diff --git a/exosphere2/program/source/smc/secmon_random_cache.hpp b/exosphere2/program/source/smc/secmon_random_cache.hpp index f3cbdc731..e1e00825c 100644 --- a/exosphere2/program/source/smc/secmon_random_cache.hpp +++ b/exosphere2/program/source/smc/secmon_random_cache.hpp @@ -19,6 +19,8 @@ namespace ams::secmon::smc { + constexpr inline size_t MaxRandomBytes = sizeof(SmcArguments) - sizeof(SmcArguments{}.r[0]); + void FillRandomCache(); void RefillRandomCache(); void GetRandomFromCache(void *dst, size_t size); diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp index dba7c4af5..861acca26 100644 --- a/exosphere2/program/source/smc/secmon_smc_common.hpp +++ b/exosphere2/program/source/smc/secmon_smc_common.hpp @@ -22,7 +22,7 @@ namespace ams::secmon::smc { Success = 0, NotImplemented = 1, InvalidArgument = 2, - InProgress = 3, + Busy = 3, NoAsyncOperation = 4, InvalidAsyncOperation = 5, NotPermitted = 6, diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp index 693080aa7..d057d27d5 100644 --- a/exosphere2/program/source/smc/secmon_smc_random.cpp +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -16,17 +16,69 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "secmon_smc_random.hpp" +#include "secmon_random_cache.hpp" +#include "secmon_smc_se_lock.hpp" namespace ams::secmon::smc { + namespace { + + SmcResult GenerateRandomBytesImpl(SmcArguments &args) { + /* Validate the input size. */ + const size_t size = args.r[1]; + + if (size > MaxRandomBytes) { + return SmcResult::InvalidArgument; + } + + /* Create a buffer that the se can generate bytes into. */ + util::AlignedBuffer<hw::DataCacheLineSize, MaxRandomBytes> buffer; + hw::FlushDataCache(buffer, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes into the buffer. */ + se::GenerateRandomBytes(buffer, size); + + /* Ensure that the cpu sees consistent data. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(buffer, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the bytes to output. */ + std::memcpy(std::addressof(args.r[1]), buffer, size); + return SmcResult::Success; + } + + } + SmcResult SmcGenerateRandomBytes(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, GenerateRandomBytesImpl); } SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Try to lock the security engine, so that we can call the standard impl. */ + if (TryLockSecurityEngine()) { + /* Ensure we unlock the security engine when done. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Take advantage of our lock to refill lthe random cache. */ + ON_SCOPE_EXIT { RefillRandomCache(); }; + + /* If we lock it successfully, we can just call the blocking impl. */ + return GenerateRandomBytesImpl(args); + } else { + /* Otherwise, we'll retrieve some bytes from the cache. */ + const size_t size = args.r[1]; + + /* Validate the input size. */ + if (size > MaxRandomBytes) { + return SmcResult::InvalidArgument; + } + + /* Get random bytes from the cache. */ + GetRandomFromCache(std::addressof(args.r[1]), size); + return SmcResult::Success; + } } } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp index d748ce70a..63b662282 100644 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp @@ -38,4 +38,41 @@ namespace ams::secmon::smc { return g_is_locked; } + SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) { + /* Try to lock the SE. */ + if (!TryLockSecurityEngine()) { + return SmcResult::Busy; + } + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + return impl(args); + } + + SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) { + SmcResult result = SmcResult::Busy; + + /* Try to lock the security engine. */ + if (TryLockSecurityEngine()) { + /* Try to start an async operation. */ + if (const u64 async_key = BeginAsyncOperation(result_handler); async_key != InvalidAsyncKey) { + /* Invoke the operation. */ + result = impl(args); + + /* If the operation was successful, return the key. */ + if (result == SmcResult::Success) { + args.r[1] = async_key; + return SmcResult::Success; + } + + /* Otherwise, cancel the async operation. */ + CancelAsyncOperation(async_key); + } + + /* We failed to invoke the async op, so unlock the security engine. */ + UnlockSecurityEngine(); + } + + return result; + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp index db8e62cf0..d7fc33d7d 100644 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp @@ -16,6 +16,8 @@ #pragma once #include <exosphere.hpp> #include "secmon_smc_common.hpp" +#include "secmon_smc_handler.hpp" +#include "secmon_smc_result.hpp" namespace ams::secmon::smc { @@ -23,4 +25,7 @@ namespace ams::secmon::smc { void UnlockSecurityEngine(); bool IsSecurityEngineLocked(); + SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl); + SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler); + } From 864b6085a8744af73d682acf928a6f13a8e7aeca Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 10:09:42 -0700 Subject: [PATCH 062/118] exo2: fix bugs in non-core0 bootup --- exosphere2/program/source/secmon_setup.cpp | 2 +- exosphere2/program/source/secmon_start_virtual.s | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 833e98b9a..02f044d4e 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -1018,7 +1018,7 @@ namespace ams::secmon { constexpr u32 ResetVectorHigh = static_cast<u32>((PhysicalTzramProgramResetVector >> BITSIZEOF(u32))); /* Write our reset vector to the secure boot registers. */ - reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_LOW, ResetVectorLow); + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_LOW, ResetVectorLow | 1); reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_HIGH, ResetVectorHigh); /* Disable non-secure writes to the reset vector. */ diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 0727929ac..37f81f118 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -59,6 +59,9 @@ _ZN3ams6secmon20StartWarmbootVirtualEv: ldr x20, =0x1F01F67C0 mov sp, x20 + /* Setup X18 to point to the global context. */ + ldr x18, =0x1F01FA000 + /* Perform final warmboot setup. */ bl _ZN3ams6secmon24SetupSocSecurityWarmbootEv From bf546d5fb3b33b59a39c36cdf9684f0749982d21 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 11:10:28 -0700 Subject: [PATCH 063/118] exo2: implement SmcGenerateAesKek, SmcLoadAesKey --- .../program/source/smc/secmon_smc_aes.cpp | 188 +++++++++++++++++- .../program/source/smc/secmon_smc_common.hpp | 6 + .../smc/secmon_smc_power_management.cpp | 8 +- .../program/source/smc/secmon_smc_random.cpp | 11 +- .../source/smc/secmon_smc_register_access.cpp | 41 ++-- .../program/source/smc/secmon_smc_se_lock.cpp | 38 ++-- .../exosphere/pkg1/pkg1_key_generation.hpp | 8 + .../exosphere/pkg1/pkg1_se_key_slots.hpp | 4 + 8 files changed, 239 insertions(+), 65 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 2edd1d812..e517e611e 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -15,18 +15,198 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_key_storage.hpp" +#include "../secmon_misc.hpp" #include "secmon_smc_aes.hpp" +#include "secmon_smc_se_lock.hpp" namespace ams::secmon::smc { + namespace { + + constexpr inline auto AesKeySize = se::AesBlockSize; + + enum SealKey { + SealKey_LoadAesKey = 0, + SealKey_DecryptDeviceUniqueData = 1, + SealKey_LoadLotusKey = 2, + SealKey_LoadEsDeviceKey = 3, + SealKey_ReencryptDeviceUniqueData = 4, + SealKey_LoadSslKey = 5, + SealKey_LoadEsClientCertKey = 6, + + SealKey_Count, + }; + + enum KeyType { + KeyType_Default = 0, + KeyType_NormalOnly = 1, + KeyType_RecoveryOnly = 2, + KeyType_NormalAndRecovery = 3, + + KeyType_Count, + }; + + struct GenerateAesKekOption { + using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>; + using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>; + using SealKeyIndex = util::BitPack32::Field<5, 3, SealKey>; + using Reserved = util::BitPack32::Field<8, 24, u32>; + }; + + constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = { + [SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 }, + [SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 }, + [SealKey_LoadLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 }, + [SealKey_LoadEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D }, + [SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE }, + [SealKey_LoadSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 }, + [SealKey_LoadEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B }, + }; + + constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = { + [KeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 }, + [KeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 }, + [KeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 }, + [KeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B }, + }; + + constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = { + [SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + [SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 }, + [SealKey_LoadLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C }, + [SealKey_LoadEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB }, + [SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 }, + [SealKey_LoadSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 }, + [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, + }; + + int PrepareMasterKey(int generation) { + if (generation == GetKeyGeneration()) { + return pkg1::AesKeySlot_Master; + } + + constexpr int Slot = pkg1::AesKeySlot_Smc; + LoadMasterKey(Slot, generation); + + return Slot; + } + + int PrepareDeviceMasterKey(int generation) { + if (generation == pkg1::KeyGeneration_1_0_0) { + return pkg1::AesKeySlot_Device; + } + if (generation == GetKeyGeneration()) { + return pkg1::AesKeySlot_DeviceMaster; + } + + constexpr int Slot = pkg1::AesKeySlot_Smc; + LoadDeviceMasterKey(Slot, generation); + + return Slot; + } + + SmcResult GenerateAesKekImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 kek_source[AesKeySize]; + std::memcpy(kek_source, std::addressof(args.r[1]), AesKeySize); + + const int generation = std::min<int>(args.r[3] - 1, pkg1::KeyGeneration_1_0_0); + + const util::BitPack32 option = { static_cast<u32>(args.r[4]) }; + const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>(); + const auto key_type = option.Get<GenerateAesKekOption::KeyTypeIndex>(); + const auto seal_key = option.Get<GenerateAesKekOption::SealKeyIndex>(); + const u32 reserved = option.Get<GenerateAesKekOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + + if (is_device_unique) { + SMC_R_UNLESS(pkg1::IsValidDeviceUniqueKeyGeneration(generation), InvalidArgument); + } else { + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + } + + SMC_R_UNLESS(0 <= key_type && key_type < KeyType_Count, InvalidArgument); + SMC_R_UNLESS(0 <= seal_key && seal_key < SealKey_Count, InvalidArgument); + + switch (key_type) { + case KeyType_NormalOnly: SMC_R_UNLESS(!IsRecoveryBoot(), InvalidArgument); break; + case KeyType_RecoveryOnly: SMC_R_UNLESS( IsRecoveryBoot(), InvalidArgument); break; + default: break; + } + + /* Declare temporary data storage. */ + u8 static_source[AesKeySize]; + u8 generated_key[AesKeySize]; + u8 access_key[AesKeySize]; + + /* Derive the static source. */ + for (size_t i = 0; i < sizeof(static_source); ++i) { + static_source[i] = KeyTypeSources[key_type][i] ^ SealKeyMasks[seal_key][i]; + } + + /* Get the seal key source. */ + const u8 * const seal_key_source = SealKeySources[seal_key]; + + /* Get the key slot. */ + const int slot = is_device_unique ? PrepareDeviceMasterKey(generation) : PrepareMasterKey(generation); + + /* Derive a static generation kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, static_source, sizeof(static_source)); + + /* Decrypt the input with the static generation kek. */ + se::DecryptAes128(generated_key, sizeof(generated_key), pkg1::AesKeySlot_Smc, kek_source, sizeof(kek_source)); + + /* Generate the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, AesKeySize); + + /* Seal the generated key. */ + se::EncryptAes128(access_key, sizeof(access_key), pkg1::AesKeySlot_Smc, generated_key, sizeof(generated_key)); + + /* Copy the access key out. */ + std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key)); + return SmcResult::Success; + } + + SmcResult LoadAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + const int slot = args.r[1]; + + u8 access_key[AesKeySize]; + std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key)); + + u8 key_source[AesKeySize]; + std::memcpy(key_source, std::addressof(args.r[4]), sizeof(key_source)); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + + /* Get the seal key source. */ + constexpr const u8 * const SealKeySource = SealKeySources[SealKey_LoadAesKey]; + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, SealKeySource, AesKeySize); + + /* Unseal the access key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key)); + + /* Derive the key. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, key_source, sizeof(key_source)); + + return SmcResult::Success; + } + + } + SmcResult SmcGenerateAesKek(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl); } SmcResult SmcLoadAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, LoadAesKeyImpl); } SmcResult SmcComputeAes(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp index 861acca26..a03658e61 100644 --- a/exosphere2/program/source/smc/secmon_smc_common.hpp +++ b/exosphere2/program/source/smc/secmon_smc_common.hpp @@ -35,6 +35,12 @@ namespace ams::secmon::smc { PsciAlreadyOn = static_cast<u32>(-4), }; + #define SMC_R_SUCCEEEDED(res) (res == SmcResult::Success) + #define SMC_R_FAILED(res) (res != SmcResult::Success) + + #define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } }) + #define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return SmcResult::RES; }}) + struct SmcArguments { u64 r[8]; }; diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 7ccd6ef77..92cbd3491 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -104,14 +104,10 @@ namespace ams::secmon::smc { SmcResult SmcPowerOnCpu(SmcArguments &args) { /* Get and validate the core to power on. */ const int which_core = args.r[1]; - if (!(0 <= which_core && which_core < NumCores)) { - return SmcResult::PsciInvalidParameters; - } + SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters); /* Ensure the core isn't already on. */ - if (IsCoreOn(which_core)) { - return SmcResult::PsciAlreadyOn; - } + SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn); /* Save the entry context. */ SetEntryContext(which_core, args.r[2], args.r[3]); diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp index d057d27d5..6b3099a6d 100644 --- a/exosphere2/program/source/smc/secmon_smc_random.cpp +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -26,10 +26,7 @@ namespace ams::secmon::smc { SmcResult GenerateRandomBytesImpl(SmcArguments &args) { /* Validate the input size. */ const size_t size = args.r[1]; - - if (size > MaxRandomBytes) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); /* Create a buffer that the se can generate bytes into. */ util::AlignedBuffer<hw::DataCacheLineSize, MaxRandomBytes> buffer; @@ -69,11 +66,7 @@ namespace ams::secmon::smc { } else { /* Otherwise, we'll retrieve some bytes from the cache. */ const size_t size = args.r[1]; - - /* Validate the input size. */ - if (size > MaxRandomBytes) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); /* Get random bytes from the cache. */ GetRandomFromCache(std::addressof(args.r[1]), size); diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp index b9e8b4d63..cdf834353 100644 --- a/exosphere2/program/source/smc/secmon_smc_register_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -150,39 +150,34 @@ namespace ams::secmon::smc { const u32 value = args.r[3]; /* Validate that the address is aligned. */ - if (!util::IsAligned(address, alignof(u32))) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(util::IsAligned(address, alignof(u32)), InvalidArgument); /* Find the access table. */ const AccessTableEntry * const entry = GetAccessTableEntry(address); - /* If we have no table, don't perform the write. */ - if (entry == nullptr) { + /* If we have a table, perform the write. */ + if (entry != nullptr) { + /* Get the address to read or write. */ + const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); + u32 out = 0; + + if (mask != ~static_cast<u32>(0)) { + out = reg::Read(virtual_address); + } + if (mask != static_cast<u32>(0)) { + reg::Write(virtual_address, (out & ~mask) | (value & mask)); + } + + args.r[1] = out; + } else { /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ - constexpr uintptr_t MC = MemoryRegionVirtualDeviceMemoryController.GetAddress(); - if (address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)) { - return SmcResult::Success; - } - - return SmcResult::InvalidArgument; + constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); } - /* Get the address to read or write. */ - const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); - u32 out = 0; - - if (mask != ~static_cast<u32>(0)) { - out = reg::Read(virtual_address); - } - if (mask != static_cast<u32>(0)) { - reg::Write(virtual_address, (out & ~mask) | (value & mask)); - } - - args.r[1] = out; return SmcResult::Success; } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp index 63b662282..b8ce15ab2 100644 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp @@ -39,40 +39,32 @@ namespace ams::secmon::smc { } SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) { - /* Try to lock the SE. */ - if (!TryLockSecurityEngine()) { - return SmcResult::Busy; - } + /* Try to lock the security engine. */ + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); ON_SCOPE_EXIT { UnlockSecurityEngine(); }; return impl(args); } SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) { - SmcResult result = SmcResult::Busy; - /* Try to lock the security engine. */ - if (TryLockSecurityEngine()) { - /* Try to start an async operation. */ - if (const u64 async_key = BeginAsyncOperation(result_handler); async_key != InvalidAsyncKey) { - /* Invoke the operation. */ - result = impl(args); + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); + auto se_guard = SCOPE_GUARD { UnlockSecurityEngine(); }; - /* If the operation was successful, return the key. */ - if (result == SmcResult::Success) { - args.r[1] = async_key; - return SmcResult::Success; - } + /* Try to start an async operation. */ + const u64 async_key = BeginAsyncOperation(result_handler); + SMC_R_UNLESS(async_key != InvalidAsyncKey, Busy); + auto async_guard = SCOPE_GUARD { CancelAsyncOperation(async_key); }; - /* Otherwise, cancel the async operation. */ - CancelAsyncOperation(async_key); - } + /* Try to invoke the operation. */ + SMC_R_TRY(impl(args)); - /* We failed to invoke the async op, so unlock the security engine. */ - UnlockSecurityEngine(); - } + /* We succeeded! Cancel our guards, and return the async key to our caller. */ + async_guard.Cancel(); + se_guard.Cancel(); - return result; + args.r[1] = async_key; + return SmcResult::Success; } } diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp index 711f36ba4..5e58876f1 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp @@ -44,4 +44,12 @@ namespace ams::pkg1 { constexpr inline const int OldMasterKeyCount = KeyGeneration_Count - 1; constexpr inline const int OldDeviceMasterKeyCount = KeyGeneration_Count - KeyGeneration_4_0_0; + constexpr bool IsValidDeviceUniqueKeyGeneration(int generation) { + return generation == KeyGeneration_1_0_0 || (KeyGeneration_4_0_0 <= generation && generation <= KeyGeneration_Current); + } + + constexpr bool IsValidKeyGeneration(int generation) { + return KeyGeneration_Min <= generation && generation <= KeyGeneration_Current; + } + } diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp index 614aa5a38..e7c7074f8 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -53,4 +53,8 @@ namespace ams::pkg1 { RsaKeySlot_PrivateKey = 1, }; + constexpr bool IsUserAesKeySlot(int slot) { + return AesKeySlot_UserStart <= slot && slot < AesKeySlot_UserEnd; + } + } From b6b114ec4097ff9fb4eefb9364c5c32028965d8a Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 12:05:17 -0700 Subject: [PATCH 064/118] exo2: implement SmcSetKernelCarveoutRegion --- exosphere2/program/source/secmon_setup.cpp | 9 +++++++++ exosphere2/program/source/secmon_setup.hpp | 4 ++++ .../program/source/smc/secmon_smc_aes.cpp | 2 +- .../program/source/smc/secmon_smc_carveout.cpp | 18 ++++++++++++++++-- .../program/source/smc/secmon_smc_handler.cpp | 2 +- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 02f044d4e..da611c9bd 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -1194,4 +1194,13 @@ namespace ams::secmon { hw::InstructionSynchronizationBarrier(); } + void SetKernelCarveoutRegion(int index, uintptr_t address, size_t size) { + /* Configure the carveout. */ + auto &carveout = g_kernel_carveouts[index]; + carveout.address = address; + carveout.size = size; + + SetupKernelCarveouts(); + } + } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp index c3c468fee..f6e1f3776 100644 --- a/exosphere2/program/source/secmon_setup.hpp +++ b/exosphere2/program/source/secmon_setup.hpp @@ -23,6 +23,8 @@ namespace ams::secmon { constexpr inline int KernelCarveoutCount = 2; + constexpr size_t CarveoutSizeMax = 512_MB - 128_KB; + void SetupCpuMemoryControllersEnableMmu(); void SetupCpuCoreContext(); void SetupCpuSErrorDebug(); @@ -37,4 +39,6 @@ namespace ams::secmon { void SaveSecurityEngineAesKeySlotTestVector(); + void SetKernelCarveoutRegion(int index, uintptr_t address, size_t size); + } \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index e517e611e..3ca701edb 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -111,7 +111,7 @@ namespace ams::secmon::smc { u8 kek_source[AesKeySize]; std::memcpy(kek_source, std::addressof(args.r[1]), AesKeySize); - const int generation = std::min<int>(args.r[3] - 1, pkg1::KeyGeneration_1_0_0); + const int generation = std::max<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0); const util::BitPack32 option = { static_cast<u32>(args.r[4]) }; const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>(); diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp index 2f947431e..120ae9029 100644 --- a/exosphere2/program/source/smc/secmon_smc_carveout.cpp +++ b/exosphere2/program/source/smc/secmon_smc_carveout.cpp @@ -15,13 +15,27 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_setup.hpp" #include "secmon_smc_carveout.hpp" namespace ams::secmon::smc { SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const int index = args.r[1]; + const uintptr_t address = args.r[2]; + const size_t size = args.r[3]; + + /* Validate arguments. */ + SMC_R_UNLESS(0 <= index && index < KernelCarveoutCount, InvalidArgument); + SMC_R_UNLESS(util::IsAligned(address, 128_KB), InvalidArgument); + SMC_R_UNLESS(util::IsAligned(size, 128_KB), InvalidArgument); + SMC_R_UNLESS(size <= CarveoutSizeMax, InvalidArgument); + + /* Set the carveout. */ + SetKernelCarveoutRegion(index, address, size); + + return SmcResult::Success; } } diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index ac960fae4..ddea4311e 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -242,7 +242,7 @@ namespace ams::secmon::smc { util::WaitMicroSeconds(1000); } - if (args.r[0] != static_cast<u64>(SmcResult::Success)) { + if (args.r[0] != static_cast<u64>(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) { *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC; *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id); for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { From e0dbfc69a8e25ad7b75ff51537f1da4101db1d44 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 14:58:45 -0700 Subject: [PATCH 065/118] exo2: implement SmcComputeAes, SmcGetResult, SmcGetResultData --- exosphere2/program/source/secmon_map.cpp | 54 +++++++ exosphere2/program/source/secmon_map.hpp | 3 + exosphere2/program/source/secmon_setup.cpp | 4 + .../program/source/smc/secmon_smc_aes.cpp | 85 ++++++++++- .../program/source/smc/secmon_smc_result.cpp | 82 ++++++++++- .../source/smc/secmon_user_page_mapper.cpp | 61 ++++++++ .../source/smc/secmon_user_page_mapper.hpp | 35 +++++ .../libexosphere/include/exosphere/gic.hpp | 2 + .../include/exosphere/se/se_aes.hpp | 4 + .../include/exosphere/se/se_management.hpp | 2 + .../exosphere/secmon/secmon_memory_layout.hpp | 1 + libraries/libexosphere/source/gic/gic_api.cpp | 4 + libraries/libexosphere/source/se/se_aes.cpp | 137 +++++++++++++++--- .../libexosphere/source/se/se_execute.cpp | 25 +++- .../libexosphere/source/se/se_execute.hpp | 3 + .../libexosphere/source/se/se_management.cpp | 8 + 16 files changed, 486 insertions(+), 24 deletions(-) create mode 100644 exosphere2/program/source/smc/secmon_user_page_mapper.cpp create mode 100644 exosphere2/program/source/smc/secmon_user_page_mapper.hpp diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index f3bd02792..f41d73ad1 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -15,6 +15,7 @@ */ #include <exosphere.hpp> #include "secmon_cache.hpp" +#include "secmon_setup.hpp" #include "secmon_map.hpp" namespace ams::secmon { @@ -24,8 +25,12 @@ namespace ams::secmon { constexpr inline const uintptr_t BootCodeAddress = MemoryRegionVirtualTzramBootCode.GetAddress(); constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize(); + constinit uintptr_t g_smc_user_page_physical_address = 0; + using namespace ams::mmu; + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); + constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { /* Unmap the L3 entries corresponding to the boot code. */ InvalidateL3Entries(l3, boot_code, boot_code_size); @@ -42,6 +47,16 @@ namespace ams::secmon { InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); } + constexpr void MapSmcUserPageImpl(u64 *l3, uintptr_t address) { + /* Set the L3 entry. */ + SetL3BlockEntry(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), address, MemoryRegionVirtualSmcUserPage.GetSize(), MappingAttributesEl3NonSecureRwData); + } + + constexpr void UnmapSmcUserPageImpl(u64 *l3) { + /* Unmap the L3 entry. */ + InvalidateL3Entries(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), MemoryRegionVirtualSmcUserPage.GetSize()); + } + void ClearLow(uintptr_t address, size_t size) { /* Clear the low part. */ util::ClearMemory(reinterpret_cast<void *>(address), size / 2); @@ -85,4 +100,43 @@ namespace ams::secmon { secmon::EnsureMappingConsistency(); } + uintptr_t MapSmcUserPage(uintptr_t address) { + if (g_smc_user_page_physical_address != 0) { + if (!(MemoryRegionDram.GetAddress() <= address && address <= MemoryRegionDramHigh.GetEndAddress() - MemoryRegionVirtualSmcUserPage.GetSize())) { + return 0; + } + if (!util::IsAligned(address, 4_KB)) { + return 0; + } + + g_smc_user_page_physical_address = address; + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + MapSmcUserPageImpl(l2_l3, address); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualSmcUserPage.GetAddress()); + } else { + AMS_ABORT_UNLESS(address == g_smc_user_page_physical_address); + } + + return MemoryRegionVirtualSmcUserPage.GetAddress(); + } + + void UnmapSmcUserPage() { + if (g_smc_user_page_physical_address == 0) { + return; + } + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + UnmapSmcUserPageImpl(l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualSmcUserPage.GetAddress()); + + g_smc_user_page_physical_address = 0; + } + } diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp index aa8c7f32e..2d74d4a8e 100644 --- a/exosphere2/program/source/secmon_map.hpp +++ b/exosphere2/program/source/secmon_map.hpp @@ -20,4 +20,7 @@ namespace ams::secmon { void UnmapTzram(); + uintptr_t MapSmcUserPage(uintptr_t address); + void UnmapSmcUserPage(); + } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index da611c9bd..6748022bf 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -20,6 +20,7 @@ #include "secmon_cpu_context.hpp" #include "secmon_interrupt_handler.hpp" #include "secmon_misc.hpp" +#include "smc/secmon_random_cache.hpp" #include "smc/secmon_smc_power_management.hpp" #include "smc/secmon_smc_se_lock.hpp" @@ -938,6 +939,9 @@ namespace ams::secmon { ExitChargerHiZMode(); } + /* Refill the random cache, which is volatile and thus wiped on warmboot. */ + smc::FillRandomCache(); + /* Unlock the security engine. */ secmon::smc::UnlockSecurityEngine(); } diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 3ca701edb..c004091ef 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -47,6 +47,13 @@ namespace ams::secmon::smc { KeyType_Count, }; + enum CipherMode { + CipherMode_CbcEncryption = 0, + CipherMode_CbcDecryption = 1, + CipherMode_Ctr = 2, + CipherMode_Cmac = 3, + }; + struct GenerateAesKekOption { using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>; using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>; @@ -54,6 +61,11 @@ namespace ams::secmon::smc { using Reserved = util::BitPack32::Field<8, 24, u32>; }; + struct ComputeAesOption { + using KeySlot = util::BitPack32::Field<0, 3, int>; + using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>; + }; + constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = { [SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 }, [SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 }, @@ -81,6 +93,40 @@ namespace ams::secmon::smc { [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, }; + constexpr uintptr_t LinkedListAddressMinimum = secmon::MemoryRegionDram.GetAddress(); + constexpr size_t LinkedListAddressRangeSize = 4_MB - 2_KB; + constexpr uintptr_t LinkedListAddressMaximum = LinkedListAddressMinimum + LinkedListAddressRangeSize; + + constexpr size_t LinkedListSize = 12; + + constexpr bool IsValidLinkedListAddress(uintptr_t address) { + return LinkedListAddressMinimum <= address && address <= (LinkedListAddressMaximum - LinkedListSize); + } + + constinit bool g_is_compute_aes_completed = false; + + void SecurityEngineDoneHandler() { + /* Check that the compute succeeded. */ + se::ValidateAesOperationResult(); + + /* End the asynchronous operation. */ + g_is_compute_aes_completed = true; + EndAsyncOperation(); + } + + SmcResult GetComputeAesResult(void *dst, size_t size) { + /* Arguments are unused. */ + AMS_UNUSED(dst); + AMS_UNUSED(size); + + /* Check that the operation is completed. */ + SMC_R_UNLESS(g_is_compute_aes_completed, Busy); + + /* Unlock the security engine and succeed. */ + UnlockSecurityEngine(); + return SmcResult::Success; + } + int PrepareMasterKey(int generation) { if (generation == GetKeyGeneration()) { return pkg1::AesKeySlot_Master; @@ -199,6 +245,42 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult ComputeAesImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 iv[se::AesBlockSize]; + + const util::BitPack32 option = { static_cast<u32>(args.r[1]) }; + std::memcpy(iv, std::addressof(args.r[2]), sizeof(iv)); + const u32 input_address = args.r[4]; + const u32 output_address = args.r[5]; + const u32 size = args.r[6]; + + const int slot = option.Get<ComputeAesOption::KeySlot>(); + const auto cipher_mode = option.Get<ComputeAesOption::CipherModeIndex>(); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + SMC_R_UNLESS(util::IsAligned(size, se::AesBlockSize), InvalidArgument); + SMC_R_UNLESS(IsValidLinkedListAddress(input_address), InvalidArgument); + SMC_R_UNLESS(IsValidLinkedListAddress(output_address), InvalidArgument); + + /* We're starting an aes operation, so reset the completion status. */ + g_is_compute_aes_completed = false; + + /* Dispatch the correct aes operation asynchronously. */ + switch (cipher_mode) { + case CipherMode_CbcEncryption: se::EncryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break; + case CipherMode_CbcDecryption: se::DecryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break; + case CipherMode_Ctr: se::ComputeAes128CtrAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break; + case CipherMode_Cmac: + return SmcResult::NotImplemented; + default: + return SmcResult::InvalidArgument; + } + + return SmcResult::Success; + } + } SmcResult SmcGenerateAesKek(SmcArguments &args) { @@ -210,8 +292,7 @@ namespace ams::secmon::smc { } SmcResult SmcComputeAes(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvokeAsync(args, ComputeAesImpl, GetComputeAesResult); } SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp index 5223ceca5..702f12ff2 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -16,17 +16,91 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "secmon_smc_result.hpp" +#include "secmon_user_page_mapper.hpp" namespace ams::secmon::smc { + namespace { + + constinit u64 g_async_key = InvalidAsyncKey; + constinit GetResultHandler g_async_handler = nullptr; + + u64 GenerateRandomU64() { + /* NOTE: This is one of the only places where Nintendo does not do data flushing. */ + /* to ensure coherency when doing random byte generation. */ + /* It is not clear why it is necessary elsewhere but not here. */ + /* TODO: Figure out why. */ + u64 v; + se::GenerateRandomBytes(std::addressof(v), sizeof(v)); + return v; + } + + } + + u64 BeginAsyncOperation(GetResultHandler handler) { + /* Only allow one async operation at a time. */ + if (g_async_key != InvalidAsyncKey) { + return InvalidAsyncKey; + } + + /* Generate a random async key. */ + g_async_key = GenerateRandomU64(); + g_async_handler = handler; + + return g_async_key; + } + + void CancelAsyncOperation(u64 async_key) { + if (async_key == g_async_key) { + g_async_key = InvalidAsyncKey; + } + } + + void EndAsyncOperation() { + gic::SetPending(SecurityEngineUserInterruptId); + } + SmcResult SmcGetResult(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const u64 async_key = args.r[1]; + + /* Validate arguments. */ + SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); + SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); + + /* Call the handler. */ + args.r[1] = static_cast<u64>(g_async_handler(nullptr, 0)); + g_async_key = InvalidAsyncKey; + + return SmcResult::Success; } SmcResult SmcGetResultData(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const u64 async_key = args.r[1]; + const uintptr_t user_phys_addr = args.r[2]; + const size_t user_size = args.r[3]; + + /* Allocate a work buffer on the stack. */ + alignas(8) u8 work_buffer[1_KB]; + + /* Validate arguments. */ + SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); + SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); + SMC_R_UNLESS(user_size <= sizeof(work_buffer), InvalidArgument); + + /* Call the handler. */ + args.r[1] = static_cast<u64>(g_async_handler(work_buffer, user_size)); + g_async_key = InvalidAsyncKey; + + /* Map the user buffer. */ + { + UserPageMapper mapper(user_phys_addr); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyToUser(user_phys_addr, work_buffer, user_size), InvalidArgument); + } + + return SmcResult::Success; } } diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.cpp b/exosphere2/program/source/smc/secmon_user_page_mapper.cpp new file mode 100644 index 000000000..3748592db --- /dev/null +++ b/exosphere2/program/source/smc/secmon_user_page_mapper.cpp @@ -0,0 +1,61 @@ +/* + * 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_map.hpp" +#include "secmon_user_page_mapper.hpp" + +namespace ams::secmon::smc { + + bool UserPageMapper::Map() { + this->virtual_address = MapSmcUserPage(this->physical_address); + return this->virtual_address != 0; + } + + void *UserPageMapper::GetPointerTo(uintptr_t phys, size_t size) const { + /* Ensure we stay within the page. */ + if (util::AlignDown(phys, 4_KB) != this->physical_address) { + return nullptr; + } + if (size != 0) { + if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) { + return nullptr; + } + } + + return reinterpret_cast<void *>(phys + (this->virtual_address - this->physical_address)); + } + + bool UserPageMapper::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { + void * const dst = this->GetPointerTo(dst_phys, size); + if (dst == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + + bool UserPageMapper::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { + const void * const src = this->GetPointerTo(src_phys, size); + if (src == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + +} diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.hpp b/exosphere2/program/source/smc/secmon_user_page_mapper.hpp new file mode 100644 index 000000000..cec6eb96d --- /dev/null +++ b/exosphere2/program/source/smc/secmon_user_page_mapper.hpp @@ -0,0 +1,35 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + class UserPageMapper { + private: + uintptr_t physical_address; + uintptr_t virtual_address; + public: + constexpr UserPageMapper(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } + + bool Map(); + void *GetPointerTo(uintptr_t phys, size_t size) const; + bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; + bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; + }; + +} diff --git a/libraries/libexosphere/include/exosphere/gic.hpp b/libraries/libexosphere/include/exosphere/gic.hpp index 72e30432c..4b866d4e4 100644 --- a/libraries/libexosphere/include/exosphere/gic.hpp +++ b/libraries/libexosphere/include/exosphere/gic.hpp @@ -36,6 +36,8 @@ namespace ams::gic { void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask); void SetSpiMode(int interrupt_id, InterruptMode mode); + void SetPending(int interrupt_id); + int GetInterruptRequestId(); void SetEndOfInterrupt(int interrupt_id); diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index a9bf3e67f..b78820416 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -35,4 +35,8 @@ namespace ams::se { void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + } diff --git a/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp index f5a8e6bea..05369d1fa 100644 --- a/libraries/libexosphere/include/exosphere/se/se_management.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -30,4 +30,6 @@ namespace ams::se { void HandleInterrupt(); + void ValidateAesOperationResult(); + } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index a7818c11c..38e6452bf 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -73,6 +73,7 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionVirtual = MemoryRegion(UINT64_C(0x1F0000000), 2_MB); constexpr inline const MemoryRegion MemoryRegionPhysical = MemoryRegion(UINT64_C( 0x40000000), 1_GB); constexpr inline const MemoryRegion MemoryRegionDram = MemoryRegion(UINT64_C( 0x80000000), 2_GB); + constexpr inline const MemoryRegion MemoryRegionDramHigh = MemoryRegion(MemoryRegionDram.GetEndAddress(), 2_GB); constexpr inline const MemoryRegion MemoryRegionDramGpuCarveout = MemoryRegion(UINT64_C(0x80020000), UINT64_C(0x40000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramGpuCarveout)); diff --git a/libraries/libexosphere/source/gic/gic_api.cpp b/libraries/libexosphere/source/gic/gic_api.cpp index 70e36002c..220713e79 100644 --- a/libraries/libexosphere/source/gic/gic_api.cpp +++ b/libraries/libexosphere/source/gic/gic_api.cpp @@ -206,6 +206,10 @@ namespace ams::gic { ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast<u32>(mode) << 1); } + void SetPending(int interrupt_id) { + Write(g_distributor_address + offsetof(GicDistributor, ispendr), 1, interrupt_id, 1); + } + int GetInterruptRequestId() { return reg::Read(GetCpuInterface()->iar); } diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index 138e18cfb..ed0ec0e91 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -33,21 +33,37 @@ namespace ams::se { MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF, }; - constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); - constexpr inline u32 AesConfigCtr = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 1), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, LINEAR_CTR), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigCtr = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 1), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, LINEAR_CTR), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + constexpr inline u32 AesConfigCbcEncrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_AESOUT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, TOP), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + constexpr inline u32 AesConfigCbcDecrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_PREV_MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) { reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), @@ -73,9 +89,9 @@ namespace ams::se { reg::ReadWrite(SE->SE_CONFIG, REG_BITS_VALUE(16, 16, mode)); } - // void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { - // reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); - // } + void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { + reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); + } void SetCounter(volatile SecurityEngineRegisters *SE, const void *ctr) { const u32 *ctr_32 = reinterpret_cast<const u32 *>(ctr); @@ -87,6 +103,25 @@ namespace ams::se { reg::Write(SE->SE_CRYPTO_LINEAR_CTR[3], util::LoadLittleEndian(ctr_32 + 3)); } + void SetAesKeyIv(volatile SecurityEngineRegisters *SE, int slot, const void *iv, size_t iv_size) { + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + AMS_ABORT_UNLESS(iv_size <= AesBlockSize); + + /* Set each iv word in order. */ + const u32 *iv_u32 = static_cast<const u32 *>(iv); + const int num_words = iv_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the key word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = *(iv_u32++); + } + } + void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) { AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount); @@ -133,6 +168,29 @@ namespace ams::se { ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { + /* If nothing to decrypt, succeed. */ + if (size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Configure for the specific operation. */ + SetConfig(SE, encrypt, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, encrypt, config); + UpdateMemoryInterface(SE, MemoryInterface_Mc); + + /* Configure the number of blocks. */ + const int num_blocks = size / AesBlockSize; + SetBlockCount(SE, num_blocks); + + /* Set the done handler. */ + SetDoneHandler(SE, handler); + + /* Start the raw operation. */ + StartOperationRaw(SE, SE_OPERATION_OP_START, out_ll_address, in_ll_address); + } + } void ClearAesKeySlot(int slot) { @@ -270,4 +328,49 @@ namespace ams::se { } } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCbcEncrypt, true, SE); + } + + void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCbcDecrypt, false, SE); + } + + void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Here Nintendo writes 1 to SE_SPARE. It's unclear why they do this, but we will do so as well. */ + SE->SE_SPARE = 0x1; + + /* Set the counter. */ + SetCounter(SE, iv); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCtr, true, SE); + } + } diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp index 35b18fcda..47252e772 100644 --- a/libraries/libexosphere/source/se/se_execute.cpp +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -64,6 +64,13 @@ namespace ams::se { reg::Write(SE->SE_OPERATION, SE_REG_BITS_VALUE(OPERATION_OP, op)); } + void EnsureOperationStarted(volatile SecurityEngineRegisters *SE) { + /* Read the operation register to make sure our write takes. */ + reg::Read(SE->SE_OPERATION); + + hw::DataSynchronizationBarrierInnerShareable(); + } + void WaitForOperationComplete(volatile SecurityEngineRegisters *SE) { /* Spin until the operation is done. */ while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_SE_OP_DONE, CLEAR))) { /* ... */ } @@ -124,6 +131,18 @@ namespace ams::se { std::memcpy(dst, aligned, dst_size); } + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) { + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, in_ll_address); + reg::Write(SE->SE_OUT_LL_ADDR, out_ll_address); + + /* Start the operation. */ + StartOperation(SE, op); + + /* Ensure the operation is started. */ + EnsureOperationStarted(SE); + } + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE) { /* Ensure no error occurred. */ AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); @@ -132,7 +151,11 @@ namespace ams::se { AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); /* Ensure there is no error status. */ - AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + } + + void ValidateAesOperationResult() { + return ValidateAesOperationResult(GetRegisters()); } } diff --git a/libraries/libexosphere/source/se/se_execute.hpp b/libraries/libexosphere/source/se/se_execute.hpp index 76b0a34af..5b242c597 100644 --- a/libraries/libexosphere/source/se/se_execute.hpp +++ b/libraries/libexosphere/source/se/se_execute.hpp @@ -23,6 +23,9 @@ namespace ams::se { void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address); + void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler); + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE); } diff --git a/libraries/libexosphere/source/se/se_management.cpp b/libraries/libexosphere/source/se/se_management.cpp index 8c145fd59..0c577a01b 100644 --- a/libraries/libexosphere/source/se/se_management.cpp +++ b/libraries/libexosphere/source/se/se_management.cpp @@ -105,4 +105,12 @@ namespace ams::se { } } + void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler) { + /* Set the done handler. */ + g_done_handler = handler; + + /* Configure to trigger an interrupt when done. */ + reg::Write(SE->SE_INT_ENABLE, SE_REG_BITS_ENUM(INT_ENABLE_SE_OP_DONE, ENABLE)); + } + } From aa50944568fb363d9736443321d5c94802181334 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 15 May 2020 17:42:04 -0700 Subject: [PATCH 066/118] exo2: tentative (read: bugged) SmcComputeCmac, SmcGenerateSpecificAesKey, SmcGetSecureData --- .../program/source/smc/secmon_smc_aes.cpp | 173 +++++++++++++++++- .../program/source/smc/secmon_smc_aes.hpp | 2 + .../program/source/smc/secmon_smc_handler.cpp | 16 +- .../libexosphere/include/exosphere/fuse.hpp | 5 + .../include/exosphere/se/se_aes.hpp | 3 + .../libexosphere/source/fuse/fuse_api.cpp | 5 + libraries/libexosphere/source/se/se_aes.cpp | 119 +++++++++++- 7 files changed, 316 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index c004091ef..b007c8b12 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -19,12 +19,14 @@ #include "../secmon_misc.hpp" #include "secmon_smc_aes.hpp" #include "secmon_smc_se_lock.hpp" +#include "secmon_user_page_mapper.hpp" namespace ams::secmon::smc { namespace { - constexpr inline auto AesKeySize = se::AesBlockSize; + constexpr inline auto AesKeySize = se::AesBlockSize; + constexpr inline size_t CmacSizeMax = 4_KB; enum SealKey { SealKey_LoadAesKey = 0, @@ -54,6 +56,22 @@ namespace ams::secmon::smc { CipherMode_Cmac = 3, }; + enum SpecificAesKey { + SpecificAesKey_CalibrationEncryption0 = 0, + SpecificAesKey_CalibrationEncryption1 = 1, + + SpecificAesKey_Count, + }; + + enum SecureData { + SecureData_Calibration = 0, + SecureData_SafeMode = 1, + SecureData_UserSystemProperEncryption = 2, + SecureData_UserSystem = 3, + + SecureData_Count, + }; + struct GenerateAesKekOption { using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>; using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>; @@ -93,6 +111,54 @@ namespace ams::secmon::smc { [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, }; + constexpr const u8 CalibrationKeySource[AesKeySize] = { + 0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95 + }; + + constexpr const u8 SecureDataSource[AesKeySize] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + constexpr const u8 SecureDataCounters[][AesKeySize] = { + [SecureData_Calibration] = { 0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9 }, + [SecureData_SafeMode] = { 0x50, 0x81, 0xCF, 0x77, 0x18, 0x11, 0xD7, 0x0D, 0x13, 0x29, 0x60, 0xED, 0x4B, 0x21, 0x3E, 0xFC }, + [SecureData_UserSystemProperEncryption] = { 0x98, 0xCB, 0x4C, 0xEB, 0x15, 0xF1, 0x4A, 0x5A, 0x7A, 0x86, 0xB6, 0xF1, 0x94, 0x66, 0xF4, 0x9D }, + }; + + constexpr const u8 SecureDataTweaks[][AesKeySize] = { + [SecureData_Calibration] = { 0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E }, + [SecureData_SafeMode] = { 0x6E, 0xF8, 0x2A, 0x1A, 0xE0, 0x4F, 0xC3, 0x20, 0x08, 0x7B, 0xBA, 0x50, 0xC0, 0xCD, 0x7B, 0x39 }, + [SecureData_UserSystemProperEncryption] = { 0x6D, 0x02, 0x56, 0x2D, 0xF4, 0x3D, 0x0A, 0x15, 0xB1, 0x34, 0x5C, 0xC2, 0x84, 0x4C, 0xD4, 0x28 }, + }; + + constexpr const u8 *GetSecureDataCounter(SecureData which) { + switch (which) { + case SecureData_Calibration: + return SecureDataCounters[SecureData_Calibration]; + case SecureData_SafeMode: + return SecureDataCounters[SecureData_SafeMode]; + case SecureData_UserSystem: + case SecureData_UserSystemProperEncryption: + return SecureDataCounters[SecureData_UserSystemProperEncryption]; + default: + return nullptr; + } + } + + constexpr const u8 *GetSecureDataTweak(SecureData which) { + switch (which) { + case SecureData_Calibration: + return SecureDataTweaks[SecureData_Calibration]; + case SecureData_SafeMode: + return SecureDataTweaks[SecureData_SafeMode]; + case SecureData_UserSystem: + case SecureData_UserSystemProperEncryption: + return SecureDataTweaks[SecureData_UserSystemProperEncryption]; + default: + return nullptr; + } + } + constexpr uintptr_t LinkedListAddressMinimum = secmon::MemoryRegionDram.GetAddress(); constexpr size_t LinkedListAddressRangeSize = 4_MB - 2_KB; constexpr uintptr_t LinkedListAddressMaximum = LinkedListAddressMinimum + LinkedListAddressRangeSize; @@ -152,6 +218,20 @@ namespace ams::secmon::smc { return Slot; } + void GetSecureDataImpl(u8 *dst, SecureData which, bool tweak) { + /* Compute the appropriate AES-CTR. */ + se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize); + + /* Tweak, if we should. */ + if (tweak) { + const u8 * const tweak = GetSecureDataTweak(which); + + for (size_t i = 0; i < AesKeySize; ++i) { + dst[i] ^= tweak[i]; + } + } + } + SmcResult GenerateAesKekImpl(SmcArguments &args) { /* Decode arguments. */ u8 kek_source[AesKeySize]; @@ -281,6 +361,85 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult GenerateSpecificAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 key_source[AesKeySize]; + std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source)); + + const int generation = GetTargetFirmware() >= TargetFirmware_4_0_0 ? std::max<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0) : pkg1::KeyGeneration_1_0_0; + const auto which = static_cast<SpecificAesKey>(args.r[4]); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(which < SpecificAesKey_Count, InvalidArgument); + + /* Generate the specific aes key. */ + u8 output_key[AesKeySize]; + if (fuse::GetPatchVersion() >= fuse::PatchVersion_Odnx02A2) { + const int slot = PrepareDeviceMasterKey(generation); + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, CalibrationKeySource, sizeof(CalibrationKeySource)); + se::DecryptAes128(output_key, sizeof(output_key), pkg1::AesKeySlot_Smc, key_source, sizeof(key_source)); + } else { + GetSecureDataImpl(output_key, SecureData_Calibration, which == SpecificAesKey_CalibrationEncryption1); + } + + /* Copy the key to output. */ + std::memcpy(std::addressof(args.r[1]), output_key, sizeof(output_key)); + return SmcResult::Success; + } + + SmcResult ComputeCmacImpl(SmcArguments &args) { + /* Decode arguments. */ + const int slot = args.r[1]; + const uintptr_t data_address = args.r[2]; + const uintptr_t data_size = args.r[3]; + + /* Declare buffer for user data. */ + alignas(8) u8 user_data[CmacSizeMax]; + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + SMC_R_UNLESS(data_size <= sizeof(user_data), InvalidArgument); + + /* Map the user data, and copy to stack. */ + { + UserPageMapper mapper(data_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(user_data, data_address, data_size), InvalidArgument); + } + + /* Ensure the SE sees consistent data. */ + hw::FlushDataCache(user_data, data_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Compute the mac. */ + { + u8 mac[se::AesBlockSize]; + se::ComputeAes128Cmac(mac, sizeof(mac), slot, user_data, data_size); + + std::memcpy(std::addressof(args.r[1]), mac, sizeof(mac)); + } + + return SmcResult::Success; + } + + SmcResult GetSecureDataImpl(SmcArguments &args) { + /* Decode arguments. */ + const auto which = static_cast<SecureData>(args.r[1]); + + /* Validate arguments/conditions. */ + SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented); + SMC_R_UNLESS(which < SecureData_Count, NotImplemented); + + /* Use a temporary buffer. */ + u8 secure_data[AesKeySize]; + GetSecureDataImpl(secure_data, which, false); + + /* Copy out. */ + std::memcpy(std::addressof(args.r[1]), secure_data, sizeof(secure_data)); + return SmcResult::Success; + } + } SmcResult SmcGenerateAesKek(SmcArguments &args) { @@ -296,13 +455,11 @@ namespace ams::secmon::smc { } SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, GenerateSpecificAesKeyImpl); } SmcResult SmcComputeCmac(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, ComputeCmacImpl); } SmcResult SmcLoadPreparedAesKey(SmcArguments &args) { @@ -310,4 +467,10 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* 'Tis the last rose of summer, / Left blooming alone; */ + /* Oh! who would inhabit / This bleak world alone? */ + SmcResult SmcGetSecureData(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, GetSecureDataImpl); + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index 8aa55fd81..067b4bf6b 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -26,4 +26,6 @@ namespace ams::secmon::smc { SmcResult SmcComputeCmac(SmcArguments &args); SmcResult SmcLoadPreparedAesKey(SmcArguments &args); + SmcResult SmcGetSecureData(SmcArguments &args); + } diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index ddea4311e..9d9b92ea6 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -140,7 +140,11 @@ namespace ams::secmon::smc { { 0xF0000201, Restriction_None, SmcIramCopy }, { 0xF0000002, Restriction_None, SmcReadWriteRegister }, { 0xF0000003, Restriction_None, SmcWriteAddress }, - { 0xF0000003, Restriction_None, SmcGetEmummcConfig }, + { 0xF0000404, Restriction_None, SmcGetEmummcConfig }, + }; + + constexpr const HandlerInfo GetSecureDataHandlerInfo = { + 0x67891234, Restriction_None, SmcGetSecureData }; constinit HandlerTable g_handler_tables[] = { @@ -163,6 +167,11 @@ namespace ams::secmon::smc { InvalidSmcError(id); } + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return g_handler_tables[HandlerType_User]; + } + /* Check if we're a user SMC. */ if (type == HandlerType_User) { /* Nintendo uses OEM SMCs. */ @@ -181,6 +190,11 @@ namespace ams::secmon::smc { } const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return GetSecureDataHandlerInfo; + } + /* Get and check the index. */ const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>(); if (AMS_UNLIKELY(index >= table.count)) { diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index e6ff9ebb9..eef79fed7 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -43,6 +43,10 @@ namespace ams::fuse { HardwareState_Undefined = 2, }; + enum PatchVersion { + PatchVersion_Odnx02A2 = (SocType_Erista << 12) | 0x07F, + }; + enum DramId { DramId_IcosaSamsung4GB = 0, DramId_IcosaHynix4GB = 1, @@ -96,6 +100,7 @@ namespace ams::fuse { HardwareType GetHardwareType(); HardwareState GetHardwareState(); u64 GetDeviceId(); + PatchVersion GetPatchVersion(); QuestState GetQuestState(); pmic::Regulator GetRegulator(); int GetDeviceUniqueKeyGeneration(); diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index b78820416..942f9155d 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -35,6 +35,9 @@ namespace ams::se { void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index c01286bfe..ce83e77c3 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -281,6 +281,11 @@ namespace ams::fuse { } } + PatchVersion GetPatchVersion() { + const auto patch_version = reg::Read(GetChipRegisters().FUSE_SOC_SPEEDO_1_CALIB); + return static_cast<PatchVersion>(static_cast<int>(GetSocType() << 12) | patch_version); + } + QuestState GetQuestState() { return static_cast<QuestState>(util::BitPack32{GetOdmWord(4)}.Get<OdmWord4::QuestState>()); } diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index ed0ec0e91..e1c8286ce 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -49,6 +49,14 @@ namespace ams::se { SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigCmac = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_AESOUT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, TOP), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, ENABLE)); + constexpr inline u32 AesConfigCbcEncrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), @@ -117,7 +125,7 @@ namespace ams::se { SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); - /* Set the key word. */ + /* Set the iv word. */ SE->SE_CRYPTO_KEYTABLE_DATA = *(iv_u32++); } } @@ -168,6 +176,107 @@ namespace ams::se { ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); } + void ExpandSubkey(u8 *subkey) { + /* Shift everything left one bit. */ + u8 prev = 0; + for (int i = AesBlockSize - 1; i >= 0; --i) { + const u8 top = (subkey[i] >> 7); + subkey[i] = ((subkey[i] << 1) | top); + prev = top; + } + + /* And xor with Rb if necessary. */ + if (prev != 0) { + subkey[AesBlockSize - 1] ^= 0x87; + } + } + + void GetCmacResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) { + const int num_words = dst_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + reg::Write(static_cast<u32 *>(dst) + i, reg::Read(SE->SE_HASH_RESULT[i])); + } + } + + void ComputeAesCmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, AesMode mode) { + /* Validate input. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine mac extents. */ + const int num_blocks = util::DivideUp(src_size, AesBlockSize); + const size_t last_block_size = (src_size == 0) ? 0 : (src_size - ((num_blocks - 1) * AesBlockSize)); + + /* Create subkey. */ + u8 subkey[AesBlockSize]; + { + /* Encrypt zeroes. */ + std::memset(subkey, 0, sizeof(subkey)); + EncryptAes(subkey, sizeof(subkey), slot, subkey, sizeof(subkey), mode); + + /* Expand. */ + ExpandSubkey(subkey); + + /* Account for last block. */ + if (last_block_size) { + ExpandSubkey(subkey); + } + } + + /* Configure for AES-CMAC. */ + SetConfig(SE, true, SE_CONFIG_DST_HASH_REG); + SetAesConfig(SE, slot, true, AesConfigCmac); + UpdateAesMode(SE, mode); + + /* Set the IV to zero. */ + for (int i = 0; i < 4; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the iv word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + + /* Handle blocks before the last. */ + if (num_blocks > 1) { + SetBlockCount(SE, num_blocks - 1); + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, src, src_size); + reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_ENUM(CRYPTO_CONFIG_IV_SELECT, UPDATED)); + } + + /* Handle the last block. */ + { + SetBlockCount(SE, 1); + + /* Create the last block. */ + u8 last_block[AesBlockSize]; + if (last_block_size < sizeof(last_block)) { + std::memset(last_block, 0, sizeof(last_block)); + last_block[last_block_size] = 0x80; + } + std::memcpy(last_block, static_cast<const u8 *>(src) + src_size - last_block_size, last_block_size); + + /* Xor with the subkey. */ + for (size_t i = 0; i < AesBlockSize; ++i) { + last_block[i] ^= subkey[i]; + } + + /* Ensure the SE sees correct data. */ + hw::FlushDataCache(last_block, sizeof(last_block)); + hw::DataSynchronizationBarrierInnerShareable(); + + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, last_block, sizeof(last_block)); + } + + /* Get the output. */ + GetCmacResult(SE, dst, dst_size); + } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { /* If nothing to decrypt, succeed. */ if (size == 0) { return; } @@ -328,6 +437,14 @@ namespace ams::se { } } + void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes128); + } + + void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes256); + } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { /* Validate the iv. */ AMS_ABORT_UNLESS(iv_size == AesBlockSize); From bb3a8a888fd5f2da6e6f4b749c480ec31dd497a3 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 03:29:50 -0700 Subject: [PATCH 067/118] exo2: fix a number of bugs, add temp debug-log code --- .../program/source/boot/secmon_boot_setup.cpp | 2 +- .../program/source/secmon_key_storage.cpp | 4 +- exosphere2/program/source/secmon_map.cpp | 2 +- .../program/source/smc/secmon_smc_aes.cpp | 2 +- .../program/source/smc/secmon_smc_handler.cpp | 43 +++++++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp index d0daa30d9..eacfd9940 100644 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -166,7 +166,7 @@ namespace ams::secmon::boot { se::DecryptAes128(work_block, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize); /* Set the old master key. */ - SetMasterKey(generation - 1, work_block, se::AesBlockSize); + SetMasterKey(i - 1, work_block, se::AesBlockSize); /* Set the old master key into a temporary keyslot. */ se::SetAesKey(pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize); diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp index 8e823deec..419b82cdb 100644 --- a/exosphere2/program/source/secmon_key_storage.cpp +++ b/exosphere2/program/source/secmon_key_storage.cpp @@ -86,7 +86,7 @@ namespace ams::secmon { } void LoadMasterKey(int slot, int generation) { - const int index = std::min(0, generation - pkg1::KeyGeneration_Min); + const int index = std::max(0, generation - pkg1::KeyGeneration_Min); se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetMasterKeyStorage(index), se::AesBlockSize); } @@ -96,7 +96,7 @@ namespace ams::secmon { } void LoadDeviceMasterKey(int slot, int generation) { - const int index = std::min(0, generation - pkg1::KeyGeneration_4_0_0); + const int index = std::max(0, generation - pkg1::KeyGeneration_4_0_0); se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetDeviceMasterKeyStorage(index), se::AesBlockSize); } diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index f41d73ad1..f8e5d9b1d 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -101,7 +101,7 @@ namespace ams::secmon { } uintptr_t MapSmcUserPage(uintptr_t address) { - if (g_smc_user_page_physical_address != 0) { + if (g_smc_user_page_physical_address == 0) { if (!(MemoryRegionDram.GetAddress() <= address && address <= MemoryRegionDramHigh.GetEndAddress() - MemoryRegionVirtualSmcUserPage.GetSize())) { return 0; } diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index b007c8b12..7d1796838 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -26,7 +26,7 @@ namespace ams::secmon::smc { namespace { constexpr inline auto AesKeySize = se::AesBlockSize; - constexpr inline size_t CmacSizeMax = 4_KB; + constexpr inline size_t CmacSizeMax = 1_KB; enum SealKey { SealKey_LoadAesKey = 0, diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 9d9b92ea6..4a27bf6d8 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -231,9 +231,48 @@ namespace ams::secmon::smc { return info.handler(args); } + constinit std::atomic<int> g_logged = 0; + + constexpr int LogMin = 0x100; + constexpr int LogMax = 0x120; + + constexpr size_t LogBufSize = 0x5000; + + void DebugLog(SmcArguments &args) { + const int current = g_logged.fetch_add(1); + + if (current == 0) { + std::memset(MemoryRegionVirtualDebug.GetPointer<void>(), 0xCC, LogBufSize); + } + + if (current < LogMin) { + return; + } + + + const int ind = current - LogMin; + const int ofs = (ind * sizeof(args)) % LogBufSize; + + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + ofs))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; + } + + if (current >= LogMax) { + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } + + } + } void HandleSmc(int type, SmcArguments &args) { + if (type == HandlerType_User) { + DebugLog(args); + } + /* Get the table. */ const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]); @@ -243,6 +282,10 @@ namespace ams::secmon::smc { /* Set the invocation result. */ args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args)); + if (type == HandlerType_User) { + DebugLog(args); + } + /* TODO: For debugging. Remove this when exo2 is complete. */ #if 1 if (args.r[0] == static_cast<u64>(SmcResult::NotImplemented)) { From a0b08d0540b7e9e008eb5c14b1caf2f7be19fb24 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 11:59:16 -0700 Subject: [PATCH 068/118] exo2: fix bugs in Cmac and DeviceId calculation --- libraries/libexosphere/source/fuse/fuse_api.cpp | 4 ++-- libraries/libexosphere/source/se/se_aes.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index ce83e77c3..920976f9e 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -200,7 +200,7 @@ namespace ams::fuse { void GetEcid(br::BootEcid *out) { /* Get the registers. */ - const volatile auto &chip = GetChipRegisters(); + volatile auto &chip = GetChipRegisters(); /* Read the ecid components. */ const u32 vendor = reg::Read(chip.FUSE_OPT_VENDOR_CODE) & ((1u << 4) - 1); @@ -224,7 +224,7 @@ namespace ams::fuse { u64 GetDeviceId() { /* Get the registers. */ - const volatile auto &chip = GetChipRegisters(); + volatile auto &chip = GetChipRegisters(); /* Read the device id components. */ /* NOTE: Device ID is "basically" just an alternate encoding of Ecid. */ diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index e1c8286ce..3ab0867b9 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -181,7 +181,7 @@ namespace ams::se { u8 prev = 0; for (int i = AesBlockSize - 1; i >= 0; --i) { const u8 top = (subkey[i] >> 7); - subkey[i] = ((subkey[i] << 1) | top); + subkey[i] = ((subkey[i] << 1) | prev); prev = top; } @@ -220,7 +220,7 @@ namespace ams::se { ExpandSubkey(subkey); /* Account for last block. */ - if (last_block_size) { + if (last_block_size != AesBlockSize) { ExpandSubkey(subkey); } } From 99e0448f301fa3a7e7626932ac244dcf00dbbf1c Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 16:34:59 -0700 Subject: [PATCH 069/118] exo2: cache soc type/hardware type for quick lookup --- .../program/source/boot/secmon_boot_setup.cpp | 2 +- exosphere2/program/source/secmon_setup.cpp | 5 ++++- ...secmon_configuration_context.arch.arm64.hpp | 16 ++++++++++++++++ .../secmon/secmon_monitor_context.hpp | 18 +++++++++++++++++- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp index eacfd9940..1f56214b6 100644 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -245,7 +245,7 @@ namespace ams::secmon::boot { void DeriveAllKeys() { /* Determine whether we're prod. */ - const bool is_prod = fuse::GetHardwareState() != fuse::HardwareState_Development; + const bool is_prod = IsProduction(); /* Get the ephemeral work block. */ u8 * const work_block = se::GetEphemeralWorkBlock(); diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index 6748022bf..f2ae08b69 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -94,6 +94,9 @@ namespace ams::secmon { /* If we don't have a valid storage context, we can just use the default one. */ ctx.secmon_cfg = DefaultSecureMonitorConfiguration; } + + /* Cache the fuse info for quick access. */ + ctx.secmon_cfg.SetFuseInfo(); } void GenerateSecurityEngineAesKeySlotTestVector(void *dst, size_t size) { @@ -387,7 +390,7 @@ namespace ams::secmon { SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE), SLAVE_SECURITY_REG_BITS_ENUM(2, DP2, ENABLE)); - const auto hw_type = fuse::GetHardwareType(); + const auto hw_type = GetHardwareType(); /* Switch Lite can't use HDMI, so set CEC to secure on hoag. */ if (hw_type == fuse::HardwareType_Hoag) { diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp index 5b2c5bdcb..fff53236b 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -100,4 +100,20 @@ namespace ams::secmon { return GetSecmonConfiguration().GetKeyGeneration(); } + ALWAYS_INLINE fuse::HardwareType GetHardwareType() { + return GetSecmonConfiguration().GetHardwareType(); + } + + ALWAYS_INLINE fuse::SocType GetSocType() { + return GetSecmonConfiguration().GetSocType(); + } + + ALWAYS_INLINE fuse::HardwareState GetHardwareState() { + return GetSecmonConfiguration().GetHardwareState(); + } + + ALWAYS_INLINE bool IsProduction() { + return GetSecmonConfiguration().IsProduction(); + } + } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 2b3822b00..263ab28f8 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -15,6 +15,7 @@ */ #pragma once #include <vapours.hpp> +#include <exosphere/fuse.hpp> #include <exosphere/secmon/secmon_emummc_context.hpp> namespace ams::secmon { @@ -48,16 +49,31 @@ namespace ams::secmon { struct SecureMonitorConfiguration { ams::TargetFirmware target_firmware; s32 key_generation; + u8 hardware_type; + u8 soc_type; + u8 hardware_state; + u8 pad_0B[1]; u32 flags; - u32 reserved[(0x80 - 0x0C) / sizeof(u32)]; + u32 reserved[(0x80 - 0x10) / sizeof(u32)]; constexpr void CopyFrom(const SecureMonitorStorageConfiguration &storage) { this->target_firmware = storage.target_firmware; this->flags = storage.flags; } + void SetFuseInfo() { + this->hardware_type = fuse::GetHardwareType(); + this->soc_type = fuse::GetSocType(); + this->hardware_state = fuse::GetHardwareState(); + } + constexpr ams::TargetFirmware GetTargetFirmware() const { return this->target_firmware; } constexpr int GetKeyGeneration() const { return this->key_generation; } + constexpr fuse::HardwareType GetHardwareType() const { return static_cast<fuse::HardwareType>(this->hardware_type); } + constexpr fuse::SocType GetSocType() const { return static_cast<fuse::SocType>(this->soc_type); } + constexpr fuse::HardwareState GetHardwareState() const { return static_cast<fuse::HardwareState>(this->hardware_state); } + + constexpr bool IsProduction() const { return this->GetHardwareState() != fuse::HardwareState_Development; } constexpr bool IsDevelopmentFunctionEnabledForKernel() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel) != 0; } constexpr bool IsDevelopmentFunctionEnabledForUser() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForUser) != 0; } From 4fe42eb99782695a9cd95199a0a0523474719c15 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 16 May 2020 16:46:34 -0700 Subject: [PATCH 070/118] exo2: implement some mariko TODOs --- exosphere2/program/sc7fw/source/sc7fw_dram.cpp | 2 +- .../program/source/boot/secmon_boot_functions.cpp | 15 ++++++++++++--- libraries/libexosphere/source/se/se_aes.cpp | 9 ++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp index dcdf9c94b..b477a7d54 100644 --- a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp +++ b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp @@ -168,7 +168,7 @@ namespace ams::sc7fw { /* Request that DPD3 pads power down. */ constexpr u32 EristaDpd3Mask = 0x0FFFFFFF; constexpr u32 MarikoDpd3Mask = 0x0FFF9FFF; - if (true /* TODO: IsErista */) { + if (fuse::GetSocType() == fuse::SocType_Erista) { RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, EristaDpd3Mask); } else { RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, MarikoDpd3Mask); diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 8773f4f4e..08e42c8cc 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -29,6 +29,17 @@ namespace ams::secmon::boot { secmon::boot::DecryptPackage2(reinterpret_cast<void *>(dst), size, reinterpret_cast<void *>(src), size, secmon::boot::GetPackage2AesKey(), crypto::AesEncryptor128::KeySize, iv, iv_size, key_generation); } + u32 GetChipId() { + constexpr u32 ChipIdErista = 0x210; + constexpr u32 ChipIdMariko = 0x214; + + switch (GetSocType()) { + case fuse::SocType_Erista: return ChipIdErista; + case fuse::SocType_Mariko: return ChipIdMariko; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message) { @@ -103,8 +114,6 @@ namespace ams::secmon::boot { constexpr u32 GpuMagicNumber = 0xC0EDBBCC; constexpr u32 SkuInfo = 0x83; constexpr u32 HdcpMicroCodeVersion = 0x2; - constexpr u32 ChipIdErista = 0x210; - constexpr u32 ChipIdMariko = 0x214; /* Get our pointers. */ u32 *gpu_magic = MemoryRegionDramGpuCarveout.GetEndPointer<u32>() - (0x004 / sizeof(*gpu_magic)); @@ -116,7 +125,7 @@ namespace ams::secmon::boot { /* Write the tsec magic numbers. */ tsec_magic[0] = SkuInfo; tsec_magic[1] = HdcpMicroCodeVersion; - tsec_magic[2] = (false /* TODO: IsMariko */) ? ChipIdMariko : ChipIdErista; + tsec_magic[2] = GetChipId(); /* Flush the magic numbers. */ hw::FlushDataCache(gpu_magic, 1 * sizeof(u32)); diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index 3ab0867b9..c46e02171 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -327,9 +327,12 @@ namespace ams::se { /* Set non per-key flags. */ if ((flags & ~KeySlotLockFlags_PerKey) != 0) { - /* TODO: KeySlotLockFlags_DstKeyTableOnly is Mariko-only. How should we handle this? */ - /* TODO: Mariko bit support. */ - reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags)); + /* KeySlotLockFlags_DstKeyTableOnly is Mariko-only. */ + if (fuse::GetSocType() == fuse::SocType_Mariko) { + reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags), REG_BITS_VALUE(7, 1, ((flags & KeySlotLockFlags_DstKeyTableOnly) != 0) ? 1 : 0)); + } else { + reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags)); + } } /* Set per-key flag. */ From 91e0bbd9d7dd547918de370ab54a8aa63b864590 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 17 May 2020 02:36:48 -0700 Subject: [PATCH 071/118] exo2: Implement (untested) SmcDecryptDeviceUniqueData --- .../program/source/secmon_key_storage.hpp | 6 +- .../program/source/smc/secmon_smc_aes.cpp | 167 +++++++++- .../program/source/smc/secmon_smc_aes.hpp | 10 + .../smc/secmon_smc_device_unique_data.cpp | 118 ++++++- .../smc/secmon_smc_device_unique_data.hpp | 7 +- .../crypto_aes_impl_security_engine.cpp | 89 +++++ .../libvapours/include/vapours/crypto.hpp | 1 + .../crypto/crypto_aes_gcm_encryptor.hpp | 73 +++++ .../vapours/crypto/crypto_gcm_encryptor.hpp | 64 ++++ .../vapours/crypto/impl/crypto_aes_impl.hpp | 8 + .../crypto/impl/crypto_gcm_mode_impl.hpp | 106 ++++++ .../include/vapours/util/util_endian.hpp | 15 +- .../impl/crypto_aes_impl.arch.arm64.cpp | 2 + .../impl/crypto_gcm_mode_impl.arch.arm64.cpp | 305 ++++++++++++++++++ 14 files changed, 925 insertions(+), 46 deletions(-) create mode 100644 libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp create mode 100644 libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp index 39d34c9da..703ae53a7 100644 --- a/exosphere2/program/source/secmon_key_storage.hpp +++ b/exosphere2/program/source/secmon_key_storage.hpp @@ -18,9 +18,11 @@ namespace ams::secmon { + /* NOTE: Lotus and EsDrmCert are switched here versus official enum, */ + /* however, this considerably simplifies logic. */ enum ImportRsaKey { - ImportRsaKey_EsDrmCert = 0, - ImportRsaKey_Lotus = 1, + ImportRsaKey_Lotus = 0, + ImportRsaKey_EsDrmCert = 1, ImportRsaKey_Ssl = 2, ImportRsaKey_EsClientCert = 3, diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 7d1796838..65af2f083 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -18,6 +18,7 @@ #include "../secmon_key_storage.hpp" #include "../secmon_misc.hpp" #include "secmon_smc_aes.hpp" +#include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_se_lock.hpp" #include "secmon_user_page_mapper.hpp" @@ -25,17 +26,19 @@ namespace ams::secmon::smc { namespace { - constexpr inline auto AesKeySize = se::AesBlockSize; - constexpr inline size_t CmacSizeMax = 1_KB; + constexpr inline auto AesKeySize = se::AesBlockSize; + constexpr inline size_t CmacSizeMax = 1_KB; + constexpr inline size_t DeviceUniqueDataSizeMin = 0x130; + constexpr inline size_t DeviceUniqueDataSizeMax = 0x240; enum SealKey { SealKey_LoadAesKey = 0, SealKey_DecryptDeviceUniqueData = 1, - SealKey_LoadLotusKey = 2, - SealKey_LoadEsDeviceKey = 3, + SealKey_ImportLotusKey = 2, + SealKey_ImportEsDeviceKey = 3, SealKey_ReencryptDeviceUniqueData = 4, - SealKey_LoadSslKey = 5, - SealKey_LoadEsClientCertKey = 6, + SealKey_ImportSslKey = 5, + SealKey_ImportEsClientCertKey = 6, SealKey_Count, }; @@ -63,6 +66,30 @@ namespace ams::secmon::smc { SpecificAesKey_Count, }; + enum DeviceUniqueData { + DeviceUniqueData_DecryptDeviceUniqueData = 0, + DeviceUniqueData_ImportLotusKey = 1, + DeviceUniqueData_ImportEsDeviceKey = 2, + DeviceUniqueData_ImportSslKey = 3, + DeviceUniqueData_ImportEsClientCertKey = 4, + + DeviceUniqueData_Count, + }; + + /* Ensure that our "subtract one" simplification is valid for the cases we care about. */ + static_assert(DeviceUniqueData_ImportLotusKey - 1 == ImportRsaKey_Lotus); + static_assert(DeviceUniqueData_ImportEsDeviceKey - 1 == ImportRsaKey_EsDrmCert); + static_assert(DeviceUniqueData_ImportSslKey - 1 == ImportRsaKey_Ssl); + static_assert(DeviceUniqueData_ImportEsClientCertKey - 1 == ImportRsaKey_EsClientCert); + + constexpr ImportRsaKey ConvertToImportRsaKey(DeviceUniqueData data) { + /* Not necessary, but if this is invoked at compile-time this will force a compile-time error. */ + AMS_ASSUME(data != DeviceUniqueData_DecryptDeviceUniqueData); + AMS_ASSUME(data < DeviceUniqueData_Count); + + return static_cast<ImportRsaKey>(static_cast<int>(data) - 1); + } + enum SecureData { SecureData_Calibration = 0, SecureData_SafeMode = 1, @@ -84,14 +111,19 @@ namespace ams::secmon::smc { using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>; }; + struct DecryptDeviceUniqueDataOption { + using DeviceUniqueDataIndex = util::BitPack32::Field<0, 3, DeviceUniqueData>; + using Reserved = util::BitPack32::Field<3, 29, u32>; + }; + constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = { [SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 }, [SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 }, - [SealKey_LoadLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 }, - [SealKey_LoadEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D }, + [SealKey_ImportLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 }, + [SealKey_ImportEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D }, [SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE }, - [SealKey_LoadSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 }, - [SealKey_LoadEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B }, + [SealKey_ImportSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 }, + [SealKey_ImportEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B }, }; constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = { @@ -104,11 +136,19 @@ namespace ams::secmon::smc { constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = { [SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, [SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 }, - [SealKey_LoadLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C }, - [SealKey_LoadEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB }, + [SealKey_ImportLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C }, + [SealKey_ImportEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB }, [SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 }, - [SealKey_LoadSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 }, - [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, + [SealKey_ImportSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 }, + [SealKey_ImportEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, + }; + + constexpr const SealKey DeviceUniqueDataToSealKey[DeviceUniqueData_Count] = { + [DeviceUniqueData_DecryptDeviceUniqueData] = SealKey_DecryptDeviceUniqueData, + [DeviceUniqueData_ImportLotusKey] = SealKey_ImportLotusKey, + [DeviceUniqueData_ImportEsDeviceKey] = SealKey_ImportEsDeviceKey, + [DeviceUniqueData_ImportSslKey] = SealKey_ImportSslKey, + [DeviceUniqueData_ImportEsClientCertKey] = SealKey_ImportEsClientCertKey, }; constexpr const u8 CalibrationKeySource[AesKeySize] = { @@ -392,7 +432,7 @@ namespace ams::secmon::smc { /* Decode arguments. */ const int slot = args.r[1]; const uintptr_t data_address = args.r[2]; - const uintptr_t data_size = args.r[3]; + const size_t data_size = args.r[3]; /* Declare buffer for user data. */ alignas(8) u8 user_data[CmacSizeMax]; @@ -423,6 +463,81 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[se::AesBlockSize]; + u8 key_source[se::AesBlockSize]; + + std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); + + const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + switch (mode) { + case DeviceUniqueData_DecryptDeviceUniqueData: + { + SMC_R_UNLESS(data_size < DeviceUniqueDataSizeMax, InvalidArgument); + } + break; + case DeviceUniqueData_ImportLotusKey: + case DeviceUniqueData_ImportEsDeviceKey: + case DeviceUniqueData_ImportSslKey: + case DeviceUniqueData_ImportEsClientCertKey: + { + SMC_R_UNLESS(DeviceUniqueDataSizeMin <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument); + } + break; + default: + return SmcResult::InvalidArgument; + } + + /* Decrypt the device unique data. */ + u8 work_buffer[DeviceUniqueDataSizeMax]; + ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); }; + { + /* Map and copy in the encrypted data. */ + UserPageMapper mapper(data_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument); + + /* Determine the seal key to use. */ + const auto seal_key_type = DeviceUniqueDataToSealKey[mode]; + const u8 * const seal_key_source = SealKeySources[seal_key_type]; + + /* Decrypt the data. */ + if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size)) { + return SmcResult::InvalidArgument; + } + + /* Either output the key, or import it. */ + switch (mode) { + case DeviceUniqueData_DecryptDeviceUniqueData: + { + SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument); + } + break; + case DeviceUniqueData_ImportLotusKey: + case DeviceUniqueData_ImportSslKey: + ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize); + break; + case DeviceUniqueData_ImportEsDeviceKey: + case DeviceUniqueData_ImportEsClientCertKey: + ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize); + ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return SmcResult::Success; + } + SmcResult GetSecureDataImpl(SmcArguments &args) { /* Decode arguments. */ const auto which = static_cast<SecureData>(args.r[1]); @@ -442,6 +557,7 @@ namespace ams::secmon::smc { } + /* Aes functionality. */ SmcResult SmcGenerateAesKek(SmcArguments &args) { return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl); } @@ -467,6 +583,27 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* Device unique data functionality. */ + SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, DecryptDeviceUniqueDataImpl); + } + + SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + /* Legacy APIs. */ + SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + /* 'Tis the last rose of summer, / Left blooming alone; */ /* Oh! who would inhabit / This bleak world alone? */ SmcResult SmcGetSecureData(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index 067b4bf6b..a0ad3adb5 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -19,6 +19,7 @@ namespace ams::secmon::smc { + /* General Aes functionality. */ SmcResult SmcGenerateAesKek(SmcArguments &args); SmcResult SmcLoadAesKey(SmcArguments &args); SmcResult SmcComputeAes(SmcArguments &args); @@ -26,6 +27,15 @@ namespace ams::secmon::smc { SmcResult SmcComputeCmac(SmcArguments &args); SmcResult SmcLoadPreparedAesKey(SmcArguments &args); + /* Device unique data functionality. */ + SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args); + SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args); + + /* Legacy device unique data functionality. */ + SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args); + SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args); + + /* The last rose of summer. */ SmcResult SmcGetSecureData(SmcArguments &args); } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp index b9ac92699..df4a227d4 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -19,25 +19,113 @@ namespace ams::secmon::smc { - SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + namespace { + + constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); + constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; + + constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize; + constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataMacSize + DeviceUniqueDataDeviceIdSize + DeviceUniqueDataPaddingSize; + constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + + void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) { + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, seal_key_source_size); + + /* Derive the device unique data kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, access_key_size); + + /* Derive the actual device unique data key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size); + } + + void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) { + /* Declare keyslot (as encryptor will need to take it by pointer/reference). */ + constexpr int Slot = pkg1::AesKeySlot_Smc; + + /* Calculate the mac. */ + crypto::Aes128GcmEncryptor gcm; + gcm.Initialize(std::addressof(Slot), sizeof(Slot), iv, iv_size); + gcm.UpdateAad(data, data_size); + gcm.GetMac(dst, dst_size); + } + + constexpr u64 GetDeviceIdLow(u64 device_id) { + /* Mask out the top byte. */ + constexpr u64 ByteMask = (static_cast<u64>(1) << BITSIZEOF(u8)) - 1; + constexpr u64 LowMask = ~(ByteMask << (BITSIZEOF(u64) - BITSIZEOF(u8))); + return device_id & LowMask; + } + + constexpr u8 GetDeviceIdHigh(u64 device_id) { + /* Get the top byte. */ + return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); + } + } - SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { + /* Determine how much decrypted data there will be. */ + const size_t enc_size = src_size - DeviceUniqueDataInnerMetaSize; + const size_t dec_size = src_size - DeviceUniqueDataOuterMetaSize; - /* Legacy APIs. */ - SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } + /* Ensure that our sizes are allowed. */ + AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize); + AMS_ABORT_UNLESS(dst_size >= enc_size); - SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Determine the extents of the data. */ + const u8 * const iv = static_cast<const u8 *>(src); + const u8 * const enc = iv + DeviceUniqueDataIvSize; + const u8 * const mac = enc + enc_size; + + /* Decrypt the data. */ + { + /* Declare temporaries. */ + u8 temp_iv[DeviceUniqueDataIvSize]; + u8 calc_mac[DeviceUniqueDataMacSize]; + ON_SCOPE_EXIT { crypto::ClearMemory(temp_iv, sizeof(temp_iv)); crypto::ClearMemory(calc_mac, sizeof(calc_mac)); }; + + /* Prepare the key used to decrypt the data. */ + PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); + + /* Copy the iv to stack. */ + std::memcpy(temp_iv, iv, sizeof(temp_iv)); + + /* Decrypt the data. */ + se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); + + /* Compute the gmac. */ + ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize); + + /* Validate the gmac. */ + if (!crypto::IsSameBytes(mac, calc_mac, sizeof(calc_mac))) { + return false; + } + } + + /* Validate device id, output device id if needed. */ + { + /* Locate the device id in the decryption output. */ + const u8 * const padding = static_cast<const u8 *>(dst) + dec_size; + const u8 * const device_id = padding + DeviceUniqueDataPaddingSize; + + /* Load the big endian device id. */ + const u64 device_id_val = util::LoadBigEndian(static_cast<const u64 *>(static_cast<const void *>(device_id))); + + /* Validate that the device id low matches the value in fuses. */ + if (GetDeviceIdLow(device_id_val) != fuse::GetDeviceId()) { + return false; + } + + /* Set the output device id high, if needed. */ + if (out_device_id_high != nullptr) { + *out_device_id_high = GetDeviceIdHigh(device_id_val); + } + } + + return true; } } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp index 1c36ec1f8..92fc5b509 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -19,11 +19,6 @@ namespace ams::secmon::smc { - SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args); - SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args); - - /* Legacy APIs. */ - SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args); - SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args); + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size); } diff --git a/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp new file mode 100644 index 000000000..d71f72440 --- /dev/null +++ b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp @@ -0,0 +1,89 @@ +/* + * 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> + +namespace ams::crypto::impl { + + namespace { + + constexpr bool IsSupportedKeySize(size_t size) { + return size == 16 || size == 24 || size == 32; + } + + } + + template<size_t KeySize> + AesImpl<KeySize>::~AesImpl() { + ClearMemory(this, sizeof(*this)); + } + + template<size_t KeySize> + void AesImpl<KeySize>::Initialize(const void *key, size_t key_size, bool is_encrypt) { + static_assert(IsSupportedKeySize(KeySize)); + + /* Set the security engine keyslot. */ + this->slot = *static_cast<const int *>(key); + } + + template<size_t KeySize> + void AesImpl<KeySize>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const { + static_assert(IsSupportedKeySize(KeySize)); + AMS_ASSERT(src_size >= BlockSize); + AMS_ASSERT(dst_size >= BlockSize); + + if constexpr (KeySize == 16) { + /* Aes 128. */ + se::EncryptAes128(dst, dst_size, this->slot, src, src_size); + } else if constexpr (KeySize == 24) { + /* Aes 192. */ + /* TODO: se::EncryptAes192(dst, dst_size, this->slot, src, src_size); */ + } else if constexpr (KeySize == 32) { + /* Aes 256. */ + /* TODO: se::EncryptAes256(dst, dst_size, this->slot, src, src_size); */ + } else { + /* Invalid key size. */ + static_assert(!std::is_same<AesImpl<KeySize>, AesImpl<KeySize>>::value); + } + } + + template<size_t KeySize> + void AesImpl<KeySize>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const { + static_assert(IsSupportedKeySize(KeySize)); + AMS_ASSERT(src_size >= BlockSize); + AMS_ASSERT(dst_size >= BlockSize); + + if constexpr (KeySize == 16) { + /* Aes 128. */ + se::DecryptAes128(dst, dst_size, this->slot, src, src_size); + } else if constexpr (KeySize == 24) { + /* Aes 192. */ + /* TODO: se::DecryptAes192(dst, dst_size, this->slot, src, src_size); */ + } else if constexpr (KeySize == 32) { + /* Aes 256. */ + /* TODO: se::DecryptAes256(dst, dst_size, this->slot, src, src_size); */ + } else { + /* Invalid key size. */ + static_assert(!std::is_same<AesImpl<KeySize>, AesImpl<KeySize>>::value); + } + } + + + /* Explicitly instantiate the three supported key sizes. */ + template class AesImpl<16>; + template class AesImpl<24>; + template class AesImpl<32>; + +} diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp index 2aa531f67..0126dc99d 100644 --- a/libraries/libvapours/include/vapours/crypto.hpp +++ b/libraries/libvapours/include/vapours/crypto.hpp @@ -25,6 +25,7 @@ #include <vapours/crypto/crypto_aes_decryptor.hpp> #include <vapours/crypto/crypto_aes_ctr_encryptor_decryptor.hpp> #include <vapours/crypto/crypto_aes_xts_encryptor_decryptor.hpp> +#include <vapours/crypto/crypto_aes_gcm_encryptor.hpp> #include <vapours/crypto/crypto_rsa_pss_sha256_verifier.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp> #include <vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp> diff --git a/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp new file mode 100644 index 000000000..326d738bb --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp @@ -0,0 +1,73 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util.hpp> +#include <vapours/crypto/crypto_aes_encryptor.hpp> +#include <vapours/crypto/crypto_gcm_encryptor.hpp> + +namespace ams::crypto { + + namespace impl { + + template<typename _AesImpl> + class AesGcmEncryptor { + NON_COPYABLE(AesGcmEncryptor); + NON_MOVEABLE(AesGcmEncryptor); + private: + using AesImpl = _AesImpl; + using GcmImpl = GcmEncryptor<AesImpl>; + public: + static constexpr size_t KeySize = AesImpl::KeySize; + static constexpr size_t BlockSize = AesImpl::BlockSize; + static constexpr size_t MacSize = AesImpl::BlockSize; + private: + AesImpl aes_impl; + GcmImpl gcm_impl; + public: + AesGcmEncryptor() { /* ... */ } + + void Initialize(const void *key, size_t key_size, const void *iv, size_t iv_size) { + this->aes_impl.Initialize(key, key_size); + this->gcm_impl.Initialize(std::addressof(this->aes_impl), iv, iv_size); + } + + void Reset(const void *iv, size_t iv_size) { + this->gcm_impl.Reset(iv, iv_size); + } + + size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) { + return this->gcm_impl.Update(dst, dst_size, src, src_size); + } + + void UpdateAad(const void *aad, size_t aad_size) { + return this->gcm_impl.UpdateAad(aad, aad_size); + } + + void GetMac(void *dst, size_t dst_size) { + return this->gcm_impl.GetMac(dst, dst_size); + } + }; + + } + + using Aes128GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor128>; + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes192GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor192>; */ + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes256GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor256>; */ + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp new file mode 100644 index 000000000..547bd739e --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp @@ -0,0 +1,64 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util.hpp> +#include <vapours/crypto/impl/crypto_gcm_mode_impl.hpp> + +namespace ams::crypto { + + /* TODO: C++20 BlockCipher concept */ + + template<typename BlockCipher> + class GcmEncryptor { + NON_COPYABLE(GcmEncryptor); + NON_MOVEABLE(GcmEncryptor); + private: + using Impl = impl::GcmModeImpl<BlockCipher>; + public: + static constexpr size_t KeySize = Impl::KeySize; + static constexpr size_t BlockSize = Impl::BlockSize; + static constexpr size_t MacSize = Impl::MacSize; + private: + Impl impl; + public: + GcmEncryptor() { /* ... */ } + + void Initialize(const BlockCipher *cipher, const void *iv, size_t iv_size) { + this->impl.Initialize(cipher); + this->impl.Reset(iv, iv_size); + } + + void Reset(const void *iv, size_t iv_size) { + this->impl.Reset(iv, iv_size); + } + + size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) { + return this->impl.Update(dst, dst_size, src, src_size); + } + + void UpdateAad(const void *aad, size_t aad_size) { + return this->impl.UpdateAad(aad, aad_size); + } + + void GetMac(void *dst, size_t dst_size) { + return this->impl.GetMac(dst, dst_size); + } + }; + +} diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp index d99b14774..51391721a 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp @@ -30,16 +30,24 @@ namespace ams::crypto::impl { static constexpr s32 RoundCount = (KeySize / 4) + 6; static constexpr size_t RoundKeySize = BlockSize * (RoundCount + 1); private: + #ifdef ATMOSPHERE_IS_EXOSPHERE + int slot; + #endif + #ifdef ATMOSPHERE_IS_STRATOSPHERE u32 round_keys[RoundKeySize / sizeof(u32)]; + #endif public: ~AesImpl(); void Initialize(const void *key, size_t key_size, bool is_encrypt); void EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; void DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; + + #ifdef ATMOSPHERE_IS_STRATOSPHERE const u8 *GetRoundKey() const { return reinterpret_cast<const u8 *>(this->round_keys); } + #endif }; /* static_assert(HashFunction<Sha1Impl>); */ diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp new file mode 100644 index 000000000..fbe974f23 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp @@ -0,0 +1,106 @@ +/* + * 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/>. + */ + +#pragma once +#include <vapours/common.hpp> +#include <vapours/assert.hpp> +#include <vapours/util.hpp> +#include <vapours/crypto/crypto_memory_clear.hpp> +#include <vapours/crypto/crypto_aes_encryptor.hpp> + +namespace ams::crypto::impl { + + template<typename BlockCipher> + class GcmModeImpl { + NON_COPYABLE(GcmModeImpl); + NON_MOVEABLE(GcmModeImpl); + public: + static constexpr size_t KeySize = BlockCipher::KeySize; + static constexpr size_t BlockSize = BlockCipher::BlockSize; + static constexpr size_t MacSize = BlockCipher::BlockSize; + private: + enum State { + State_None, + State_Initialized, + State_ProcessingAad, + State_Encrypting, + State_Decrypting, + State_Done, + }; + + struct Block128 { + u64 hi; + u64 lo; + + ALWAYS_INLINE void Clear() { + this->hi = 0; + this->lo = 0; + } + }; + static_assert(util::is_pod<Block128>::value); + static_assert(sizeof(Block128) == 0x10); + + union Block { + Block128 block_128; + u32 block_32[4]; + u8 block_8[16]; + }; + static_assert(util::is_pod<Block>::value); + static_assert(sizeof(Block) == 0x10); + + using CipherFunction = void (*)(void *dst_block, const void *src_block, const void *ctx); + private: + State state; + const BlockCipher *block_cipher; + CipherFunction cipher_func; + u8 pad[sizeof(u64)]; + Block block_x; + Block block_y; + Block block_ek; + Block block_ek0; + Block block_tmp; + size_t aad_size; + size_t msg_size; + u32 aad_remaining; + u32 msg_remaining; + u32 counter; + Block h_mult_blocks[16]; + public: + GcmModeImpl() : state(State_None) { /* ... */ } + + ~GcmModeImpl() { + ClearMemory(this, sizeof(*this)); + } + + void Initialize(const BlockCipher *block_cipher); + + void Reset(const void *iv, size_t iv_size); + + void UpdateAad(const void *aad, size_t aad_size); + size_t UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); + size_t UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); + + void GetMac(void *dst, size_t dst_size); + private: + static void ProcessBlock(void *dst_block, const void *src_block, const void *ctx) { + static_cast<const BlockCipher *>(ctx)->EncryptBlock(dst_block, BlockSize, src_block, BlockSize); + } + + void InitializeHashKey(); + void ComputeMac(bool encrypt); + }; + +} diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp index f269796d2..147a73f80 100644 --- a/libraries/libvapours/include/vapours/util/util_endian.hpp +++ b/libraries/libvapours/include/vapours/util/util_endian.hpp @@ -43,7 +43,6 @@ namespace ams::util { ((u & (ByteMask << 16)) << 24) | ((u & (ByteMask << 8)) << 40) | ((u & (ByteMask << 0)) << 56); - } else if constexpr (std::is_same<U, u32>::value) { return ((u & (ByteMask << 24)) >> 24) | ((u & (ByteMask << 16)) >> 8) | @@ -79,7 +78,7 @@ namespace ams::util { constexpr ALWAYS_INLINE void SwapBytes(T *ptr) { using U = typename std::make_unsigned<T>::type; - *ptr = static_cast<T>(SwapBytes(static_cast<U>(*ptr))); + *ptr = static_cast<T>(SwapBytes<U>(static_cast<U>(*ptr))); } template<typename T> requires std::integral<T> @@ -90,7 +89,7 @@ namespace ams::util { return static_cast<T>(static_cast<U>(val)); } else { static_assert(IsLittleEndian()); - return static_cast<T>(SwapBytes(static_cast<U>(val))); + return static_cast<T>(SwapBytes<U>(static_cast<U>(val))); } } @@ -99,7 +98,7 @@ namespace ams::util { using U = typename std::make_unsigned<T>::type; if constexpr (IsBigEndian()) { - return static_cast<T>(SwapBytes(static_cast<U>(val))); + return static_cast<T>(SwapBytes<U>(static_cast<U>(val))); } else { static_assert(IsLittleEndian()); return static_cast<T>(static_cast<U>(val)); @@ -136,22 +135,22 @@ namespace ams::util { template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) { - return ConvertToBigEndian(*ptr); + return ConvertToBigEndian<T>(*ptr); } template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) { - return ConvertToLittleEndian(*ptr); + return ConvertToLittleEndian<T>(*ptr); } template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) { - *ptr = ConvertToBigEndian(val); + *ptr = ConvertToBigEndian<T>(val); } template<typename T> requires std::integral<T> constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) { - *ptr = ConvertToLittleEndian(val); + *ptr = ConvertToLittleEndian<T>(val); } } diff --git a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp index 8c7465bed..e1ad2c81f 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp @@ -111,6 +111,8 @@ namespace ams::crypto::impl { #else + /* NOTE: Exosphere defines this in libexosphere. */ + /* TODO: Non-EL0 implementation. */ #endif diff --git a/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp new file mode 100644 index 000000000..1b7c2995c --- /dev/null +++ b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp @@ -0,0 +1,305 @@ +/* + * 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 <vapours.hpp> + +#if defined(ATMOSPHERE_IS_STRATOSPHERE) + +/* TODO: EL0 implementation. */ +namespace ams::crypto::impl { + +} + +#else + +/* EL1+ implementation. */ +namespace ams::crypto::impl { + + namespace { + + constexpr u64 GetMultiplyFactor(u8 value) { + constexpr size_t Shift = BITSIZEOF(u8) - 1; + constexpr u8 Mask = (1u << Shift); + return (value & Mask) >> Shift; + } + + /* TODO: Big endian support, eventually? */ + constexpr void GaloisShiftLeft(u64 *block) { + /* Shift the block left by one. */ + block[1] <<= 1; + block[1] |= (block[0] & (static_cast<u64>(1) << (BITSIZEOF(u64) - 1))) >> (BITSIZEOF(u64) - 1); + block[0] <<= 1; + } + + constexpr u8 GaloisShiftRight(u64 *block) { + /* Determine the mask to return. */ + constexpr u8 GaloisFieldMask = 0xE1; + const u8 mask = (block[0] & 1) * GaloisFieldMask; + + /* Shift the block right by one. */ + block[0] >>= 1; + block[0] |= (block[1] & 1) << (BITSIZEOF(u64) - 1); + block[1] >>= 1; + + /* Return the mask. */ + return mask; + } + + /* Multiply two 128-bit numbers X, Y in the GF(128) Galois Field. */ + void GaloisFieldMult(void *dst, const void *x, const void *y) { + /* Our block size is 16 bytes (for a 128-bit integer). */ + constexpr size_t BlockSize = 16; + constexpr size_t FieldSize = 128; + + /* Declare work blocks for us to store temporary values. */ + u8 x_block[BlockSize]; + u8 y_block[BlockSize]; + u8 out[BlockSize]; + + /* Declare 64-bit pointers for our convenience. */ + u64 *x_64 = static_cast<u64 *>(static_cast<void *>(x_block)); + u64 *y_64 = static_cast<u64 *>(static_cast<void *>(y_block)); + u64 *out_64 = static_cast<u64 *>(static_cast<void *>(out)); + + /* Initialize our work blocks. */ + for (size_t i = 0; i < BlockSize; ++i) { + x_block[i] = static_cast<const u8 *>(x)[BlockSize - 1 - i]; + y_block[i] = static_cast<const u8 *>(y)[BlockSize - 1 - i]; + out[i] = 0; + } + + /* Perform multiplication on each bit in y. */ + for (size_t i = 0; i < FieldSize; ++i) { + /* Get the multiply factor for this bit. */ + const auto y_mult = GetMultiplyFactor(y_block[BlockSize - 1]); + + /* Multiply x by the factor. */ + out_64[0] ^= x_64[0] * y_mult; + out_64[1] ^= x_64[1] * y_mult; + + /* Shift left y by one. */ + GaloisShiftLeft(y_64); + + /* Shift right x by one, and mask appropriately. */ + const u8 x_mask = GaloisShiftRight(x_64); + x_block[BlockSize - 1] ^= x_mask; + } + + /* Copy out our result. */ + for (size_t i = 0; i < BlockSize; ++i) { + static_cast<u8 *>(dst)[i] = out[BlockSize - 1 - i]; + } + } + + } + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::Initialize(const BlockCipher *block_cipher) { + /* Set member variables. */ + this->block_cipher = block_cipher; + this->cipher_func = std::addressof(GcmModeImpl<BlockCipher>::ProcessBlock); + + /* Pre-calculate values to speed up galois field multiplications later. */ + this->InitializeHashKey(); + + /* Note that we're initialized. */ + this->state = State_Initialized; + } + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::Reset(const void *iv, size_t iv_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->state >= State_Initialized); + + /* Reset blocks. */ + this->block_x.block_128.Clear(); + this->block_tmp.block_128.Clear(); + + /* Clear sizes. */ + this->aad_size = 0; + this->msg_size = 0; + this->aad_remaining = 0; + this->msg_remaining = 0; + + /* Update our state. */ + this->state = State_ProcessingAad; + + /* Set our iv. */ + if (iv_size == 12) { + /* If our iv is the correct size, simply copy in the iv, and set the magic bit. */ + std::memcpy(std::addressof(this->block_ek0), iv, iv_size); + util::StoreBigEndian(this->block_ek0.block_32 + 3, static_cast<u32>(1)); + } else { + /* Clear our ek0 block. */ + this->block_ek0.block_128.Clear(); + + /* Update using the iv as aad. */ + this->UpdateAad(iv, iv_size); + + /* Treat the iv as fake msg for the mac that will become our iv. */ + this->msg_size = this->aad_size; + this->aad_size = 0; + + /* Compute a non-final mac. */ + this->ComputeMac(false); + + /* Set our ek0 block to our calculated mac block. */ + this->block_ek0 = this->block_x; + + /* Clear our calculated mac block. */ + this->block_x.block_128.Clear(); + + /* Reset our state. */ + this->msg_size = 0; + this->aad_size = 0; + this->msg_remaining = 0; + this->aad_remaining = 0; + } + + /* Set the working block to the iv. */ + this->block_ek = this->block_ek0; + } + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::UpdateAad(const void *aad, size_t aad_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(this->state == State_ProcessingAad); + AMS_ASSERT(this->msg_size == 0); + + /* Update our aad size. */ + this->aad_size += aad_size; + + /* Define a working tracker variable. */ + const u8 *cur_aad = static_cast<const u8 *>(aad); + + /* Process any leftover aad data from a previous invocation. */ + if (this->aad_remaining > 0) { + while (aad_size > 0) { + /* Copy in a byte of the aad to our partial block. */ + this->block_x.block_8[BlockSize - 1 - this->aad_remaining] ^= *(cur_aad++); + + /* Note that we consumed a byte. */ + --aad_size; + + /* Increment our partial block size. */ + this->aad_remaining = (this->aad_remaining + 1) % BlockSize; + + /* If we have a complete block, process it and move onward. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + } + } + + /* Process as many blocks as we can. */ + while (aad_size >= BlockSize) { + /* Xor the current aad into our work block. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + } + + /* Multiply the blocks in our galois field. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + + /* Note that we've processed a block. */ + aad_size -= BlockSize; + } + + /* Update our state with whatever aad is left over. */ + if (aad_size > 0) { + /* Note how much left over data we have. */ + this->aad_remaining = static_cast<u32>(aad_size); + + /* Xor the data in. */ + for (size_t i = 0; i < aad_size; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + } + } + } + + /* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + /* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::GetMac(void *dst, size_t dst_size) { + /* Validate pre-conditions. */ + AMS_ASSERT(State_ProcessingAad <= this->state && this->state <= State_Done); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size >= MacSize); + AMS_ASSERT(this->aad_remaining == 0); + AMS_ASSERT(this->msg_remaining == 0); + + /* If we haven't already done so, compute the final mac. */ + if (this->state != State_Done) { + this->ComputeMac(true); + this->state = State_Done; + } + + static_assert(sizeof(this->block_x) == MacSize); + std::memcpy(dst, std::addressof(this->block_x), MacSize); + } + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::InitializeHashKey() { + /* We want to encrypt an empty block to use for intermediate calculations. */ + /* NOTE: Non-EL1 implementations will do multiple encryptions ahead of time, */ + /* to speed up galois field arithmetic. */ + constexpr const Block EmptyBlock = {}; + + this->ProcessBlock(std::addressof(this->h_mult_blocks[0]), std::addressof(EmptyBlock), this->block_cipher); + } + + template<class BlockCipher> + void GcmModeImpl<BlockCipher>::ComputeMac(bool encrypt) { + /* If we have leftover data, process it. */ + if (this->aad_remaining > 0 || this->msg_remaining > 0) { + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + } + + /* Setup the last block. */ + Block last_block = Block{ .block_128 = { this->msg_size, this->aad_size } }; + + /* Multiply the last block by 8 to account for bit vs byte sizes. */ + static_assert(offsetof(Block128, hi) == 0); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + GaloisShiftLeft(std::addressof(last_block.block_128.hi)); + + /* Xor the data in. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[BlockSize - 1 - i] ^= last_block.block_8[i]; + } + + /* Perform the final multiplication. */ + GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); + + /* If we need to do an encryption, do so. */ + { + /* Encrypt the iv. */ + u8 enc_result[BlockSize]; + this->ProcessBlock(enc_result, std::addressof(this->block_ek0), this->block_cipher); + + /* Xor the iv in. */ + for (size_t i = 0; i < BlockSize; ++i) { + this->block_x.block_8[i] ^= enc_result[i]; + } + } + } + + /* Explicitly instantiate the valid template classes. */ + template class GcmModeImpl<AesEncryptor128>; + +} + +#endif From 1fb9407d5dd22ee75aec8e871a645a7fd6ca6b0e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 17 May 2020 23:17:47 -0700 Subject: [PATCH 072/118] fusee/sept: changes for exo2 (note: not final) --- fusee/fusee-secondary/src/key_derivation.c | 12 +++------ fusee/fusee-secondary/src/key_derivation.h | 3 +-- fusee/fusee-secondary/src/nxboot.c | 2 +- fusee/fusee-secondary/src/nxboot_iram.c | 31 ---------------------- fusee/fusee-secondary/src/se.h | 4 +-- sept/sept-secondary/src/key_derivation.c | 10 +++---- 6 files changed, 12 insertions(+), 50 deletions(-) diff --git a/fusee/fusee-secondary/src/key_derivation.c b/fusee/fusee-secondary/src/key_derivation.c index 57982b1bf..b101c7f34 100644 --- a/fusee/fusee-secondary/src/key_derivation.c +++ b/fusee/fusee-secondary/src/key_derivation.c @@ -227,9 +227,9 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui case ATMOSPHERE_TARGET_FIRMWARE_8_1_0: case ATMOSPHERE_TARGET_FIRMWARE_9_0_0: decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10); - decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); - decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10); - decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10); + decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); + decrypt_data_into_keyslot(0xD, 0xC, masterkey_seed, 0x10); + decrypt_data_into_keyslot(0xC, 0xC, masterkey_4x_seed, 0x10); break; default: return -1; @@ -239,12 +239,6 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui return mkey_detect_revision(fuse_get_retail_type() != 0); } -/* Sets final keyslot flags, for handover to TZ/Exosphere. Setting these will prevent the BPMP from using the device key or master key. */ -void finalize_nx_keydata(uint32_t target_firmware) { - set_aes_keyslot_flags(0xC, 0xFF); - set_aes_keyslot_flags((target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (KEYSLOT_SWITCH_4XOLDDEVICEKEY) : (KEYSLOT_SWITCH_DEVICEKEY), 0xFF); -} - static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware, uint32_t generation) { unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (devkey_get_keyslot(generation)) : (KEYSLOT_SWITCH_DEVICEKEY); diff --git a/fusee/fusee-secondary/src/key_derivation.h b/fusee/fusee-secondary/src/key_derivation.h index 14108115f..48ec076a2 100644 --- a/fusee/fusee-secondary/src/key_derivation.h +++ b/fusee/fusee-secondary/src/key_derivation.h @@ -13,7 +13,7 @@ * 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 FUSEE_KEYDERIVATION_H #define FUSEE_KEYDERIVATION_H @@ -49,7 +49,6 @@ typedef struct nx_keyblob_t { int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_key, void *tsec_root_key, unsigned int *out_keygen_type); int load_package1_key(uint32_t revision); -void finalize_nx_keydata(uint32_t target_firmware); void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware); #endif diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 1a50ba754..28ec5fb34 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -957,7 +957,7 @@ uint32_t nxboot_main(void) { if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { exosphere_memaddr = (void *)0x4002D000; } else { - exosphere_memaddr = (void *)0x4002B000; + exosphere_memaddr = (void *)0x40030000; } /* Copy Exosphère to a good location or read it directly to it. */ diff --git a/fusee/fusee-secondary/src/nxboot_iram.c b/fusee/fusee-secondary/src/nxboot_iram.c index bc40d9afd..17df294c5 100644 --- a/fusee/fusee-secondary/src/nxboot_iram.c +++ b/fusee/fusee-secondary/src/nxboot_iram.c @@ -29,37 +29,6 @@ void nxboot_finish(uint32_t boot_memaddr) { uint32_t target_firmware = MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware; - volatile tegra_se_t *se = se_get_regs(); - - /* Clear used keyslots. */ - clear_aes_keyslot(KEYSLOT_SWITCH_PACKAGE2KEY); - clear_aes_keyslot(KEYSLOT_SWITCH_RNGKEY); - - /* Lock keyslots. */ - set_aes_keyslot_flags(KEYSLOT_SWITCH_MASTERKEY, 0xFF); - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); - } else { - set_aes_keyslot_flags(KEYSLOT_SWITCH_4XOLDDEVICEKEY, 0xFF); - } - - /* Finalize the GPU UCODE carveout. */ - /* NOTE: [4.0.0+] This is now done in the Secure Monitor. */ - /* mc_config_carveout_finalize(); */ - - /* Lock AES keyslots. */ - for (uint32_t i = 0; i < 16; i++) - set_aes_keyslot_flags(i, 0x15); - - /* Lock RSA keyslots. */ - for (uint32_t i = 0; i < 2; i++) - set_rsa_keyslot_flags(i, 1); - - /* Lock the Security Engine. */ - se->SE_TZRAM_SECURITY = 0; - se->SE_CRYPTO_SECURITY_PERKEY = 0; - se->SE_RSA_SECURITY_PERKEY = 0; - se->SE_SE_SECURITY &= 0xFFFFFFFB; /* Boot up Exosphère. */ MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(target_firmware) = 0; diff --git a/fusee/fusee-secondary/src/se.h b/fusee/fusee-secondary/src/se.h index 9bcc33996..cb7c3ca28 100644 --- a/fusee/fusee-secondary/src/se.h +++ b/fusee/fusee-secondary/src/se.h @@ -26,8 +26,8 @@ #define KEYSLOT_SWITCH_TEMPKEY 0x9 #define KEYSLOT_SWITCH_SESSIONKEY 0xA #define KEYSLOT_SWITCH_RNGKEY 0xB -#define KEYSLOT_SWITCH_MASTERKEY 0xC -#define KEYSLOT_SWITCH_DEVICEKEY 0xD +#define KEYSLOT_SWITCH_MASTERKEY 0xD +#define KEYSLOT_SWITCH_DEVICEKEY 0xC /* This keyslot was added in 4.0.0. */ #define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD diff --git a/sept/sept-secondary/src/key_derivation.c b/sept/sept-secondary/src/key_derivation.c index e273170bb..94287d8f5 100644 --- a/sept/sept-secondary/src/key_derivation.c +++ b/sept/sept-secondary/src/key_derivation.c @@ -63,11 +63,11 @@ void load_keys(const uint8_t *se_state) { /* Clear keyslot 0xB. */ clear_aes_keyslot(0xB); - /* Copy master key out of state keyslot 0xC into keyslot 0xC. */ - set_aes_keyslot(0xC, se_state + 0x30 + (0xC * 0x20), 0x10); + /* Copy firmware device key out of state keyslot 0xE into keyslot 0xC. */ + set_aes_keyslot(0xC, se_state + 0x30 + (0xE * 0x20), 0x10); - /* Copy firmware device key out of state keyslot 0xE into keyslot 0xD. */ - set_aes_keyslot(0xD, se_state + 0x30 + (0xE * 0x20), 0x10); + /* Copy master key out of state keyslot 0xC into keyslot 0xD. */ + set_aes_keyslot(0xD, se_state + 0x30 + (0xC * 0x20), 0x10); /* Clear keyslot 0xE. */ clear_aes_keyslot(0xE); @@ -77,5 +77,5 @@ void load_keys(const uint8_t *se_state) { /* Set keyslot flags properly in preparation for secmon. */ set_aes_keyslot_flags(0xE, 0x15); - set_aes_keyslot_flags(0xD, 0x15); + set_aes_keyslot_flags(0xC, 0x15); } From b922dff414e29f90fcbef19f3b94b78b06badb1a Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 17 May 2020 23:19:55 -0700 Subject: [PATCH 073/118] exo2: tweak debug for better logging --- .../program/source/smc/secmon_smc_handler.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 4a27bf6d8..f1cd4b37a 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -233,8 +233,8 @@ namespace ams::secmon::smc { constinit std::atomic<int> g_logged = 0; - constexpr int LogMin = 0x100; - constexpr int LogMax = 0x120; + constexpr int LogMin = 0x200; + constexpr int LogMax = 0x400; constexpr size_t LogBufSize = 0x5000; @@ -269,20 +269,20 @@ namespace ams::secmon::smc { } void HandleSmc(int type, SmcArguments &args) { - if (type == HandlerType_User) { - DebugLog(args); - } - /* Get the table. */ const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]); + if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { + DebugLog(args); + } + /* Get the handler info. */ const auto &info = GetHandlerInfo(table, args.r[0]); /* Set the invocation result. */ args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args)); - if (type == HandlerType_User) { + if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { DebugLog(args); } From 1e0124fb6752b4641c5dc6c806d142bbb6a5f829 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 18 May 2020 00:37:39 -0700 Subject: [PATCH 074/118] exo2: fix bugs in device unique data decrypytion --- .../smc/secmon_smc_device_unique_data.cpp | 25 +++++++++++++++---- .../impl/crypto_gcm_mode_impl.arch.arm64.cpp | 8 +++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp index df4a227d4..e14dfbe18 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -26,8 +26,8 @@ namespace ams::secmon::smc { constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; - constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize; - constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataMacSize + DeviceUniqueDataDeviceIdSize + DeviceUniqueDataPaddingSize; + constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; + constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) { @@ -41,6 +41,21 @@ namespace ams::secmon::smc { se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size); } + void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + /* Ensure that the SE sees consistent data. */ + hw::FlushDataCache(src, src_size); + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Use the security engine to transform the data. */ + se::ComputeAes128Ctr(dst, dst_size, slot, src, src_size, iv, iv_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Ensure the CPU sees consistent data. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) { /* Declare keyslot (as encryptor will need to take it by pointer/reference). */ constexpr int Slot = pkg1::AesKeySlot_Smc; @@ -68,8 +83,8 @@ namespace ams::secmon::smc { bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { /* Determine how much decrypted data there will be. */ - const size_t enc_size = src_size - DeviceUniqueDataInnerMetaSize; - const size_t dec_size = src_size - DeviceUniqueDataOuterMetaSize; + const size_t enc_size = src_size - DeviceUniqueDataOuterMetaSize; + const size_t dec_size = enc_size - DeviceUniqueDataInnerMetaSize; /* Ensure that our sizes are allowed. */ AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize); @@ -94,7 +109,7 @@ namespace ams::secmon::smc { std::memcpy(temp_iv, iv, sizeof(temp_iv)); /* Decrypt the data. */ - se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); + ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); /* Compute the gmac. */ ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize); diff --git a/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp index 1b7c2995c..9b6ba6c4b 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp @@ -188,7 +188,7 @@ namespace ams::crypto::impl { if (this->aad_remaining > 0) { while (aad_size > 0) { /* Copy in a byte of the aad to our partial block. */ - this->block_x.block_8[BlockSize - 1 - this->aad_remaining] ^= *(cur_aad++); + this->block_x.block_8[this->aad_remaining] ^= *(cur_aad++); /* Note that we consumed a byte. */ --aad_size; @@ -205,7 +205,7 @@ namespace ams::crypto::impl { while (aad_size >= BlockSize) { /* Xor the current aad into our work block. */ for (size_t i = 0; i < BlockSize; ++i) { - this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + this->block_x.block_8[i] ^= *(cur_aad++); } /* Multiply the blocks in our galois field. */ @@ -222,7 +222,7 @@ namespace ams::crypto::impl { /* Xor the data in. */ for (size_t i = 0; i < aad_size; ++i) { - this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++); + this->block_x.block_8[i] ^= *(cur_aad++); } } } @@ -285,7 +285,7 @@ namespace ams::crypto::impl { GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0])); /* If we need to do an encryption, do so. */ - { + if (encrypt) { /* Encrypt the iv. */ u8 enc_result[BlockSize]; this->ProcessBlock(enc_result, std::addressof(this->block_ek0), this->block_cipher); From 36754e2c38162a11ab2dcd2ce404b13fac350853 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 20 May 2020 00:34:01 -0700 Subject: [PATCH 075/118] exo2: implement the atmosphere extension mappers --- exosphere2/program/source/secmon_map.cpp | 146 +++++++++++++++++- exosphere2/program/source/secmon_map.hpp | 8 + ...er_page_mapper.hpp => secmon_spinlock.hpp} | 17 +- exosphere2/program/source/secmon_spinlock.s | 44 ++++++ .../program/source/secmon_start_virtual.s | 23 +-- .../program/source/smc/secmon_page_mapper.cpp | 80 ++++++++++ .../program/source/smc/secmon_page_mapper.hpp | 73 +++++++++ .../program/source/smc/secmon_smc_aes.cpp | 2 +- .../program/source/smc/secmon_smc_info.cpp | 13 +- .../program/source/smc/secmon_smc_info.hpp | 3 + .../program/source/smc/secmon_smc_result.cpp | 2 +- .../source/smc/secmon_user_page_mapper.cpp | 61 -------- 12 files changed, 370 insertions(+), 102 deletions(-) rename exosphere2/program/source/{smc/secmon_user_page_mapper.hpp => secmon_spinlock.hpp} (53%) create mode 100644 exosphere2/program/source/secmon_spinlock.s create mode 100644 exosphere2/program/source/smc/secmon_page_mapper.cpp create mode 100644 exosphere2/program/source/smc/secmon_page_mapper.hpp delete mode 100644 exosphere2/program/source/smc/secmon_user_page_mapper.cpp diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index f8e5d9b1d..3f6c5b4e0 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -16,7 +16,9 @@ #include <exosphere.hpp> #include "secmon_cache.hpp" #include "secmon_setup.hpp" +#include "secmon_spinlock.hpp" #include "secmon_map.hpp" +#include "smc/secmon_smc_info.hpp" namespace ams::secmon { @@ -26,10 +28,16 @@ namespace ams::secmon { constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize(); constinit uintptr_t g_smc_user_page_physical_address = 0; + constinit uintptr_t g_ams_iram_page_physical_address = 0; + constinit uintptr_t g_ams_user_page_physical_address = 0; + + constinit SpinLockType g_ams_iram_page_spin_lock = {}; + constinit SpinLockType g_ams_user_page_spin_lock = {}; using namespace ams::mmu; constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice); constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { /* Unmap the L3 entries corresponding to the boot code. */ @@ -57,6 +65,26 @@ namespace ams::secmon { InvalidateL3Entries(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), MemoryRegionVirtualSmcUserPage.GetSize()); } + constexpr void MapAtmosphereIramPageImpl(u64 *l3, uintptr_t address) { + /* Set the L3 entry. */ + SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), address, MemoryRegionVirtualAtmosphereIramPage.GetSize(), MappingAttributesEl3NonSecureDevice); + } + + constexpr void UnmapAtmosphereIramPageImpl(u64 *l3) { + /* Unmap the L3 entry. */ + InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), MemoryRegionVirtualAtmosphereIramPage.GetSize()); + } + + constexpr void MapAtmosphereUserPageImpl(u64 *l3, uintptr_t address) { + /* Set the L3 entry. */ + SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), address, MemoryRegionVirtualAtmosphereUserPage.GetSize(), MappingAttributesEl3NonSecureRwData); + } + + constexpr void UnmapAtmosphereUserPageImpl(u64 *l3) { + /* Unmap the L3 entry. */ + InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), MemoryRegionVirtualAtmosphereUserPage.GetSize()); + } + void ClearLow(uintptr_t address, size_t size) { /* Clear the low part. */ util::ClearMemory(reinterpret_cast<void *>(address), size / 2); @@ -67,6 +95,10 @@ namespace ams::secmon { util::ClearMemory(reinterpret_cast<void *>(address + size / 2), size / 2); } + bool IsPhysicalMemoryAddress(uintptr_t address) { + return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); + } + } void ClearBootCodeHigh() { @@ -88,6 +120,15 @@ namespace ams::secmon { secmon::EnsureMappingConsistency(); } + size_t GetPhysicalMemorySize() { + switch (smc::GetPhysicalMemorySize()) { + case pkg1::MemorySize_4GB: return 4_GB; + case pkg1::MemorySize_6GB: return 6_GB; + case pkg1::MemorySize_8GB: return 8_GB; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + void UnmapTzram() { /* Get the tables. */ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer<u64>(); @@ -102,9 +143,10 @@ namespace ams::secmon { uintptr_t MapSmcUserPage(uintptr_t address) { if (g_smc_user_page_physical_address == 0) { - if (!(MemoryRegionDram.GetAddress() <= address && address <= MemoryRegionDramHigh.GetEndAddress() - MemoryRegionVirtualSmcUserPage.GetSize())) { + if (!IsPhysicalMemoryAddress(address)) { return 0; } + if (!util::IsAligned(address, 4_KB)) { return 0; } @@ -139,4 +181,106 @@ namespace ams::secmon { g_smc_user_page_physical_address = 0; } + uintptr_t MapAtmosphereIramPage(uintptr_t address) { + /* Acquire the ams iram spinlock. */ + AcquireSpinLock(g_ams_iram_page_spin_lock); + auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_iram_page_spin_lock); }; + + /* Validate that the page is an IRAM page. */ + if (!MemoryRegionPhysicalIram.Contains(address, 1)) { + return 0; + } + + /* Validate that the page is aligned. */ + if (!util::IsAligned(address, 4_KB)) { + return 0; + } + + /* Map the page. */ + g_ams_iram_page_physical_address = address; + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + MapAtmosphereIramPageImpl(l2_l3, address); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress()); + + /* Hold the lock. */ + lock_guard.Cancel(); + + return true; + } + + void UnmapAtmosphereIramPage() { + /* Can't unmap if nothing's unmapped. */ + if (g_ams_iram_page_physical_address == 0) { + return; + } + + /* Unmap the page. */ + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + UnmapAtmosphereIramPageImpl(l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress()); + + /* Release the page. */ + g_ams_iram_page_physical_address = 0; + + ReleaseSpinLock(g_ams_iram_page_spin_lock); + } + + uintptr_t MapAtmosphereUserPage(uintptr_t address) { + /* Acquire the ams user spinlock. */ + AcquireSpinLock(g_ams_user_page_spin_lock); + auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_user_page_spin_lock); }; + + /* Validate that the page is a dram page. */ + if (!IsPhysicalMemoryAddress(address)) { + return 0; + } + + /* Validate that the page is aligned. */ + if (!util::IsAligned(address, 4_KB)) { + return 0; + } + + /* Map the page. */ + g_ams_user_page_physical_address = address; + + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + MapAtmosphereUserPageImpl(l2_l3, address); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress()); + + /* Hold the lock. */ + lock_guard.Cancel(); + + return true; + } + + void UnmapAtmosphereUserPage() { + /* Can't unmap if nothing's unmapped. */ + if (g_ams_user_page_physical_address == 0) { + return; + } + + /* Unmap the page. */ + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); + + UnmapAtmosphereUserPageImpl(l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress()); + + /* Release the page. */ + g_ams_user_page_physical_address = 0; + + ReleaseSpinLock(g_ams_user_page_spin_lock); + } + } diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp index 2d74d4a8e..a6bdbc405 100644 --- a/exosphere2/program/source/secmon_map.hpp +++ b/exosphere2/program/source/secmon_map.hpp @@ -18,9 +18,17 @@ namespace ams::secmon { + size_t GetPhysicalMemorySize(); + void UnmapTzram(); uintptr_t MapSmcUserPage(uintptr_t address); void UnmapSmcUserPage(); + uintptr_t MapAtmosphereIramPage(uintptr_t address); + void UnmapAtmosphereIramPage(); + + uintptr_t MapAtmosphereUserPage(uintptr_t address); + void UnmapAtmosphereUserPage(); + } \ No newline at end of file diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.hpp b/exosphere2/program/source/secmon_spinlock.hpp similarity index 53% rename from exosphere2/program/source/smc/secmon_user_page_mapper.hpp rename to exosphere2/program/source/secmon_spinlock.hpp index cec6eb96d..3c095da72 100644 --- a/exosphere2/program/source/smc/secmon_user_page_mapper.hpp +++ b/exosphere2/program/source/secmon_spinlock.hpp @@ -15,21 +15,12 @@ */ #pragma once #include <exosphere.hpp> -#include "secmon_smc_common.hpp" -namespace ams::secmon::smc { +namespace ams::secmon { - class UserPageMapper { - private: - uintptr_t physical_address; - uintptr_t virtual_address; - public: - constexpr UserPageMapper(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } + using SpinLockType = u32; - bool Map(); - void *GetPointerTo(uintptr_t phys, size_t size) const; - bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; - bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; - }; + void AcquireSpinLock(SpinLockType &lock); + void ReleaseSpinLock(SpinLockType &lock); } diff --git a/exosphere2/program/source/secmon_spinlock.s b/exosphere2/program/source/secmon_spinlock.s new file mode 100644 index 000000000..0e4ef7f64 --- /dev/null +++ b/exosphere2/program/source/secmon_spinlock.s @@ -0,0 +1,44 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams6secmon15AcquireSpinLockERj, "ax", %progbits +.align 4 +.global _ZN3ams6secmon15AcquireSpinLockERj +_ZN3ams6secmon15AcquireSpinLockERj: + /* Prepare to try to take the spinlock. */ + mov w1, #1 + sevl + prfm pstl1keep, [x0] + +1: /* Repeatedly try to take the lock. */ + wfe + ldaxr w2, [x0] + cbnz w2, 1b + stxr w2, w1, [x0] + cbnz w2, 1b + + /* Return. */ + ret + +.section .text._ZN3ams6secmon15ReleaseSpinLockERj, "ax", %progbits +.align 4 +.global _ZN3ams6secmon15ReleaseSpinLockERj +_ZN3ams6secmon15ReleaseSpinLockERj: + /* Release the spinlock. */ + stlr wzr, [x0] + + /* Return. */ + ret diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 37f81f118..8d23a66df 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -148,20 +148,8 @@ _ZN3ams6secmon25AcquireCommonSmcStackLockEv: /* Get the address of the lock. */ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE - /* Prepare to try to take the spinlock. */ - mov w1, #1 - sevl - prfm pstl1keep, [x0] - -1: /* Repeatedly try to take the lock. */ - wfe - ldaxr w2, [x0] - cbnz w2, 1b - stxr w2, w1, [x0] - cbnz w2, 1b - - /* Return. */ - ret + /* Take the lock. */ + b _ZN3ams6secmon15AcquireSpinLockERj .section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits .align 4 @@ -170,11 +158,8 @@ _ZN3ams6secmon25ReleaseCommonSmcStackLockEv: /* Get the address of the lock. */ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE - /* Release the spinlock. */ - stlr wzr, [x0] - - /* Return. */ - ret + /* Release the lock. */ + b _ZN3ams6secmon15ReleaseSpinLockERj .section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits .align 4 diff --git a/exosphere2/program/source/smc/secmon_page_mapper.cpp b/exosphere2/program/source/smc/secmon_page_mapper.cpp new file mode 100644 index 000000000..8d2008907 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_page_mapper.cpp @@ -0,0 +1,80 @@ +/* + * 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_map.hpp" +#include "secmon_page_mapper.hpp" + +namespace ams::secmon::smc { + + namespace impl { + + void *PageMapperImpl::GetPointerTo(uintptr_t phys, size_t size) const { + /* Ensure we stay within the page. */ + if (util::AlignDown(phys, 4_KB) != this->physical_address) { + return nullptr; + } + if (size != 0) { + if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) { + return nullptr; + } + } + + return reinterpret_cast<void *>(phys + (this->virtual_address - this->physical_address)); + } + + bool PageMapperImpl::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { + void * const dst = this->GetPointerTo(dst_phys, size); + if (dst == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + + bool PageMapperImpl::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { + const void * const src = this->GetPointerTo(src_phys, size); + if (src == nullptr) { + return false; + } + + std::memcpy(dst, src, size); + return true; + } + + } + + bool UserPageMapper::Map() { + return this->MapImpl<MapSmcUserPage>(); + } + + bool AtmosphereIramPageMapper::Map() { + return this->MapImpl<MapAtmosphereIramPage>(); + } + + bool AtmosphereUserPageMapper::Map() { + return this->MapImpl<MapAtmosphereUserPage>(); + } + + AtmosphereIramPageMapper::~AtmosphereIramPageMapper() { + this->UnmapImpl<UnmapAtmosphereIramPage>(); + } + + AtmosphereUserPageMapper::~AtmosphereUserPageMapper() { + this->UnmapImpl<UnmapAtmosphereUserPage>(); + } + +} diff --git a/exosphere2/program/source/smc/secmon_page_mapper.hpp b/exosphere2/program/source/smc/secmon_page_mapper.hpp new file mode 100644 index 000000000..a57f70812 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_page_mapper.hpp @@ -0,0 +1,73 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + namespace impl { + + class PageMapperImpl { + private: + uintptr_t physical_address; + uintptr_t virtual_address; + public: + constexpr PageMapperImpl(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } + + void *GetPointerTo(uintptr_t phys, size_t size) const; + bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; + bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; + + template<auto F> + bool MapImpl() { + this->virtual_address = F(this->physical_address); + return this->virtual_address != 0; + } + + template<auto F> + void UnmapImpl() { + F(); + this->virtual_address = 0; + } + }; + + } + + class UserPageMapper : public impl::PageMapperImpl { + public: + constexpr UserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + + bool Map(); + }; + + class AtmosphereIramPageMapper : public impl::PageMapperImpl { + public: + constexpr AtmosphereIramPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + ~AtmosphereIramPageMapper(); + + bool Map(); + }; + + class AtmosphereUserPageMapper : public impl::PageMapperImpl { + public: + constexpr AtmosphereUserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ } + ~AtmosphereUserPageMapper(); + + bool Map(); + }; + +} diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 65af2f083..03b2b1d49 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -20,7 +20,7 @@ #include "secmon_smc_aes.hpp" #include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_se_lock.hpp" -#include "secmon_user_page_mapper.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index 08540e09e..ae6f0ccc3 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -97,12 +97,6 @@ namespace ams::secmon::smc { return pkg1::MemoryMode_Auto; } - pkg1::MemorySize GetPhysicalMemorySize() { - const auto dram_id = fuse::GetDramId(); - AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); - return DramIdToMemorySize[dram_id]; - } - pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) { return std::min(GetPhysicalMemorySize(), size); } @@ -294,4 +288,11 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* For exosphere's usage. */ + pkg1::MemorySize GetPhysicalMemorySize() { + const auto dram_id = fuse::GetDramId(); + AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); + return DramIdToMemorySize[dram_id]; + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp index 26d28e17b..d45bf57d2 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -56,4 +56,7 @@ namespace ams::secmon::smc { /* This is an atmosphere extension smc. */ SmcResult SmcGetEmummcConfig(SmcArguments &args); + /* For other parts of exosphere. */ + pkg1::MemorySize GetPhysicalMemorySize(); + } diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp index 702f12ff2..83fa1b404 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -16,7 +16,7 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "secmon_smc_result.hpp" -#include "secmon_user_page_mapper.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_user_page_mapper.cpp b/exosphere2/program/source/smc/secmon_user_page_mapper.cpp deleted file mode 100644 index 3748592db..000000000 --- a/exosphere2/program/source/smc/secmon_user_page_mapper.cpp +++ /dev/null @@ -1,61 +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 <exosphere.hpp> -#include "../secmon_map.hpp" -#include "secmon_user_page_mapper.hpp" - -namespace ams::secmon::smc { - - bool UserPageMapper::Map() { - this->virtual_address = MapSmcUserPage(this->physical_address); - return this->virtual_address != 0; - } - - void *UserPageMapper::GetPointerTo(uintptr_t phys, size_t size) const { - /* Ensure we stay within the page. */ - if (util::AlignDown(phys, 4_KB) != this->physical_address) { - return nullptr; - } - if (size != 0) { - if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) { - return nullptr; - } - } - - return reinterpret_cast<void *>(phys + (this->virtual_address - this->physical_address)); - } - - bool UserPageMapper::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { - void * const dst = this->GetPointerTo(dst_phys, size); - if (dst == nullptr) { - return false; - } - - std::memcpy(dst, src, size); - return true; - } - - bool UserPageMapper::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { - const void * const src = this->GetPointerTo(src_phys, size); - if (src == nullptr) { - return false; - } - - std::memcpy(dst, src, size); - return true; - } - -} From 985e97cf780f618c6c2c2074ec778de83b94aa63 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 20 May 2020 04:42:42 -0700 Subject: [PATCH 076/118] exo2: implement SmcGetEmummcConfig --- exosphere2/program/source/secmon_map.cpp | 4 +- .../program/source/smc/secmon_smc_handler.cpp | 4 +- .../program/source/smc/secmon_smc_info.cpp | 55 ++++++++++++++++++- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index 3f6c5b4e0..92100ad2f 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -209,7 +209,7 @@ namespace ams::secmon { /* Hold the lock. */ lock_guard.Cancel(); - return true; + return MemoryRegionVirtualAtmosphereIramPage.GetAddress(); } void UnmapAtmosphereIramPage() { @@ -260,7 +260,7 @@ namespace ams::secmon { /* Hold the lock. */ lock_guard.Cancel(); - return true; + return MemoryRegionVirtualAtmosphereUserPage.GetAddress(); } void UnmapAtmosphereUserPage() { diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index f1cd4b37a..8abe3a71a 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -233,8 +233,8 @@ namespace ams::secmon::smc { constinit std::atomic<int> g_logged = 0; - constexpr int LogMin = 0x200; - constexpr int LogMax = 0x400; + constexpr int LogMin = 0x4000; + constexpr int LogMax = 0x4200; constexpr size_t LogBufSize = 0x5000; diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index ae6f0ccc3..c94bd7d1d 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -16,6 +16,7 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "../secmon_misc.hpp" +#include "secmon_page_mapper.hpp" #include "secmon_smc_info.hpp" #include "secmon_smc_power_management.hpp" @@ -284,8 +285,58 @@ namespace ams::secmon::smc { /* This is an atmosphere extension smc. */ SmcResult SmcGetEmummcConfig(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const auto mmc = static_cast<EmummcMmc>(args.r[1]); + const uintptr_t user_address = args.r[2]; + const uintptr_t user_offset = user_address % 4_KB; + + /* Validate arguments. */ + /* NOTE: In the future, configuration for non-NAND storage may be implemented. */ + SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotImplemented); + SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument); + + /* Get the emummc config. */ + const auto &cfg = GetEmummcConfiguration(); + static_assert(sizeof(cfg.file_cfg) == sizeof(EmummcFilePath)); + static_assert(sizeof(cfg.emu_dir_path) == sizeof(EmummcFilePath)); + + /* Clear the output. */ + constexpr size_t InlineOutputSize = sizeof(args) - sizeof(args.r[0]); + u8 * const inline_output = static_cast<u8 *>(static_cast<void *>(std::addressof(args.r[1]))); + std::memset(inline_output, 0, InlineOutputSize); + + /* Copy out the configuration. */ + { + /* Map the user output page. */ + AtmosphereUserPageMapper mapper(user_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + + /* Copy the base configuration. */ + static_assert(sizeof(cfg.base_cfg) <= InlineOutputSize); + std::memcpy(inline_output, std::addressof(cfg.base_cfg), sizeof(cfg.base_cfg)); + + /* Copy out type-specific data. */ + switch (cfg.base_cfg.type) { + case EmummcType_None: + /* No additional configuration needs to be copied. */ + break; + case EmummcType_Partition: + /* Copy the partition config. */ + static_assert(sizeof(cfg.base_cfg) + sizeof(cfg.partition_cfg) <= InlineOutputSize); + std::memcpy(inline_output + sizeof(cfg.base_cfg), std::addressof(cfg.partition_cfg), sizeof(cfg.partition_cfg)); + break; + case EmummcType_File: + /* Copy the file config. */ + SMC_R_UNLESS(mapper.CopyToUser(user_address, std::addressof(cfg.file_cfg), sizeof(cfg.file_cfg)), InvalidArgument); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Copy the redirection directory path to the user page. */ + SMC_R_UNLESS(mapper.CopyToUser(user_address + sizeof(EmummcFilePath), std::addressof(cfg.emu_dir_path), sizeof(cfg.emu_dir_path)), InvalidArgument); + } + + return SmcResult::Success; } /* For exosphere's usage. */ From ccba70abfe5a3bbeedd85754b1396ceda94ca411 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 20 May 2020 06:03:07 -0700 Subject: [PATCH 077/118] exo2: implement SmcPrepareEsDeviceUniqueKey, SmcPrepareEsCommonTitleKey, SmcLoadPreparedAesKey --- .../program/source/smc/secmon_smc_aes.cpp | 91 ++++++++++- .../program/source/smc/secmon_smc_aes.hpp | 12 ++ .../program/source/smc/secmon_smc_es.cpp | 32 ---- .../program/source/smc/secmon_smc_handler.cpp | 7 +- .../program/source/smc/secmon_smc_rsa.cpp | 152 ++++++++++++++++++ .../program/source/smc/secmon_smc_rsa.hpp | 2 + .../libexosphere/include/exosphere/se.hpp | 1 + .../include/exosphere/se/se_oaep.hpp | 15 +- .../include/exosphere/se/se_rsa.hpp | 4 + .../libexosphere/source/se/se_execute.cpp | 19 +++ .../libexosphere/source/se/se_execute.hpp | 1 + libraries/libexosphere/source/se/se_oaep.cpp | 122 ++++++++++++++ libraries/libexosphere/source/se/se_rsa.cpp | 53 ++++++ 13 files changed, 461 insertions(+), 50 deletions(-) delete mode 100644 exosphere2/program/source/smc/secmon_smc_es.cpp rename exosphere2/program/source/smc/secmon_smc_es.hpp => libraries/libexosphere/include/exosphere/se/se_oaep.hpp (66%) create mode 100644 libraries/libexosphere/source/se/se_oaep.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 03b2b1d49..aab91f842 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -155,6 +155,15 @@ namespace ams::secmon::smc { 0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95 }; + constexpr const u8 EsCommonKeySources[EsCommonKeyType_Count][AesKeySize] = { + [EsCommonKeyType_TitleKey] = { 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B }, + [EsCommonKeyType_ArchiveKey] = { 0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D }, + }; + + constexpr const u8 EsSealKeySource[AesKeySize] = { + 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 + }; + constexpr const u8 SecureDataSource[AesKeySize] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -463,6 +472,51 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult LoadPreparedAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[AesKeySize]; + + const int slot = args.r[1]; + std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key)); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource)); + + /* Unseal the key. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key)); + + return SmcResult::Success; + } + + SmcResult PrepareEsCommonTitleKeyImpl(SmcArguments &args) { + /* Declare variables. */ + u8 key_source[se::AesBlockSize]; + u8 key[se::AesBlockSize]; + u8 access_key[se::AesBlockSize]; + + /* Decode arguments. */ + std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source)); + const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast<int>(args.r[3]) - 1) : 0; + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + + /* Derive the key. */ + DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), EsCommonKeyType_TitleKey, generation); + + /* Prepare the aes key. */ + PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); + + /* Copy the access key to output. */ + std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key)); + + return SmcResult::Success; + } + SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { /* Decode arguments. */ u8 access_key[se::AesBlockSize]; @@ -530,6 +584,7 @@ namespace ams::secmon::smc { case DeviceUniqueData_ImportEsClientCertKey: ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize); ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize); + CommitRsaKeyModulus(ConvertToImportRsaKey(mode)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } @@ -579,8 +634,11 @@ namespace ams::secmon::smc { } SmcResult SmcLoadPreparedAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, LoadPreparedAesKeyImpl); + } + + SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, PrepareEsCommonTitleKeyImpl); } /* Device unique data functionality. */ @@ -604,6 +662,35 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* Es encryption utilities. */ + void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) { + /* Validate pre-conditions. */ + AMS_ABORT_UNLESS(dst_size == AesKeySize); + AMS_ABORT_UNLESS(src_size == AesKeySize); + AMS_ABORT_UNLESS(0 <= type && type < EsCommonKeyType_Count); + + /* Prepare the master key for the generation. */ + const int slot = PrepareMasterKey(generation); + + /* Derive the es common key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], AesKeySize); + + /* Decrypt the input using the common key. */ + se::DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size); + } + + void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Validate pre-conditions. */ + AMS_ABORT_UNLESS(dst_size == AesKeySize); + AMS_ABORT_UNLESS(src_size == AesKeySize); + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource)); + + /* Seal the key. */ + se::EncryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size); + } + /* 'Tis the last rose of summer, / Left blooming alone; */ /* Oh! who would inhabit / This bleak world alone? */ SmcResult SmcGetSecureData(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index a0ad3adb5..b502ee657 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -19,6 +19,13 @@ namespace ams::secmon::smc { + enum EsCommonKeyType { + EsCommonKeyType_TitleKey = 0, + EsCommonKeyType_ArchiveKey = 1, + + EsCommonKeyType_Count, + }; + /* General Aes functionality. */ SmcResult SmcGenerateAesKek(SmcArguments &args); SmcResult SmcLoadAesKey(SmcArguments &args); @@ -26,6 +33,7 @@ namespace ams::secmon::smc { SmcResult SmcGenerateSpecificAesKey(SmcArguments &args); SmcResult SmcComputeCmac(SmcArguments &args); SmcResult SmcLoadPreparedAesKey(SmcArguments &args); + SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args); /* Device unique data functionality. */ SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args); @@ -35,6 +43,10 @@ namespace ams::secmon::smc { SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args); SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args); + /* Es encryption utilities. */ + void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation); + void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size); + /* The last rose of summer. */ SmcResult SmcGetSecureData(SmcArguments &args); diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp deleted file mode 100644 index 939e12b44..000000000 --- a/exosphere2/program/source/smc/secmon_smc_es.cpp +++ /dev/null @@ -1,32 +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 <exosphere.hpp> -#include "../secmon_error.hpp" -#include "secmon_smc_es.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } - - SmcResult SmcPrepareEsCommonKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 8abe3a71a..c2577806c 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -22,7 +22,6 @@ #include "secmon_smc_carveout.hpp" #include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_error.hpp" -#include "secmon_smc_es.hpp" #include "secmon_smc_info.hpp" #include "secmon_smc_memory_access.hpp" #include "secmon_smc_power_management.hpp" @@ -120,7 +119,7 @@ namespace ams::secmon::smc { { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey }, { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey }, { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey }, - { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonKey } + { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey } }; constinit HandlerInfo g_kern_handlers[] = { @@ -233,8 +232,8 @@ namespace ams::secmon::smc { constinit std::atomic<int> g_logged = 0; - constexpr int LogMin = 0x4000; - constexpr int LogMax = 0x4200; + constexpr int LogMin = 0x1000000; + constexpr int LogMax = 0x1000000; constexpr size_t LogBufSize = 0x5000; diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp index 881a9ea65..aa1e33165 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -15,10 +15,158 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_key_storage.hpp" +#include "secmon_smc_aes.hpp" #include "secmon_smc_rsa.hpp" +#include "secmon_smc_se_lock.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { + namespace { + + struct PrepareEsDeviceUniqueKeyOption { + using KeyGeneration = util::BitPack32::Field<0, 6, int>; + using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>; + using Reserved = util::BitPack32::Field<7, 25, u32>; + }; + + class PrepareEsDeviceUniqueKeyAsyncArguments { + private: + int generation; + EsCommonKeyType type; + u8 label_digest[crypto::Sha256Generator::HashSize]; + public: + void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) { + this->generation = gen; + this->type = t; + std::memcpy(this->label_digest, ld, sizeof(this->label_digest)); + } + + int GetKeyGeneration() const { return this->generation; } + EsCommonKeyType GetCommonKeyType() const { return this->type; } + void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); } + }; + + class ModularExponentiateByStorageKeyAsyncArguments { + private: + u8 msg[se::RsaSize]; + public: + void Set(const void *m, size_t m_size) { + std::memcpy(this->msg, m, sizeof(this->msg)); + } + + void GetMessage(void *dst, size_t dst_size) const { std::memcpy(dst, this->msg, sizeof(this->msg)); } + }; + + constinit bool g_exp_mod_completed = false; + + constinit union { + ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key; + PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key; + } g_async_arguments; + + ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() { + return g_async_arguments.modular_exponentiate_by_storage_key; + } + + ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() { + return g_async_arguments.prepare_es_device_unique_key; + } + + void SecurityEngineDoneHandler() { + /* End the asynchronous operation. */ + g_exp_mod_completed = true; + EndAsyncOperation(); + } + + SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + + const uintptr_t msg_address = args.r[1]; + const uintptr_t mod_address = args.r[2]; + std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest)); + const util::BitPack32 option = { static_cast<u32>(args.r[7]) }; + + const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : 0; + const auto type = option.Get<PrepareEsDeviceUniqueKeyOption::Type>(); + const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so the operation is not completed. */ + g_exp_mod_completed = false; + + /* Set the async arguments. */ + GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest); + + /* Load the es drm key into the security engine. */ + SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized); + + /* Trigger the asynchronous modular exponentiation. */ + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + + return SmcResult::Success; + } + + SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) { + /* Declare variables. */ + u8 key_source[se::AesBlockSize]; + u8 key[se::AesBlockSize]; + u8 access_key[se::AesBlockSize]; + + /* Validate state. */ + SMC_R_UNLESS(g_exp_mod_completed, Busy); + SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument); + + /* We want to relinquish our security engine lock at the end of scope. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Get the async args. */ + const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments(); + + /* Get the exponentiation output. */ + alignas(8) u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Decode the key. */ + { + /* Get the label digest. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + async_args.GetLabelDigest(label_digest); + + /* Decode the key source. */ + const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest)); + SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument); + } + + /* Decrypt the key. */ + DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration()); + PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); + + /* Copy the access key to output. */ + std::memcpy(dst, access_key, sizeof(access_key)); + + return SmcResult::Success; + } + + } + SmcResult SmcModularExponentiate(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; @@ -29,4 +177,8 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { + return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult); + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp index cfa4301fe..63859a94f 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.hpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp @@ -22,4 +22,6 @@ namespace ams::secmon::smc { SmcResult SmcModularExponentiate(SmcArguments &args); SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args); + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); + } diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp index c46693815..a86396ccd 100644 --- a/libraries/libexosphere/include/exosphere/se.hpp +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -20,6 +20,7 @@ #include <exosphere/se/se_management.hpp> #include <exosphere/se/se_aes.hpp> #include <exosphere/se/se_hash.hpp> +#include <exosphere/se/se_oaep.hpp> #include <exosphere/se/se_rsa.hpp> #include <exosphere/se/se_rng.hpp> #include <exosphere/se/se_suspend.hpp> diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/libraries/libexosphere/include/exosphere/se/se_oaep.hpp similarity index 66% rename from exosphere2/program/source/smc/secmon_smc_es.hpp rename to libraries/libexosphere/include/exosphere/se/se_oaep.hpp index a9d2d30d9..b7488bd70 100644 --- a/exosphere2/program/source/smc/secmon_smc_es.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_oaep.hpp @@ -14,19 +14,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #pragma once -#include <exosphere.hpp> -#include "secmon_smc_common.hpp" +#include <vapours.hpp> -namespace ams::secmon::smc { +namespace ams::se { - enum EsKeyType { - EsKeyType_TitleKey = 0, - EsKeyType_ArchiveKey = 1, - - EsKeyType_Count = 2, - }; - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); - SmcResult SmcPrepareEsCommonKey(SmcArguments &args); + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size); } diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp index e632f67a9..c9733c5ee 100644 --- a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -15,6 +15,7 @@ */ #pragma once #include <vapours.hpp> +#include <exosphere/se/se_common.hpp> namespace ams::se { @@ -27,5 +28,8 @@ namespace ams::se { void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler); + + void GetRsaResult(void *dst, size_t dst_size); } diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp index 47252e772..4f811ee8a 100644 --- a/libraries/libexosphere/source/se/se_execute.cpp +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -131,6 +131,25 @@ namespace ams::se { std::memcpy(dst, aligned, dst_size); } + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size) { + /* Set the linked list entry. */ + LinkedListEntry src_entry; + SetLinkedListEntry(std::addressof(src_entry), src, src_size); + + /* Ensure the linked list entry data is seen correctly. */ + hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(src_entry)))); + + /* Start the operation. */ + StartOperation(SE, SE_OPERATION_OP_START); + + /* Ensure the operation is started. */ + EnsureOperationStarted(SE); + } + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) { /* Configure the linked list addresses. */ reg::Write(SE->SE_IN_LL_ADDR, in_ll_address); diff --git a/libraries/libexosphere/source/se/se_execute.hpp b/libraries/libexosphere/source/se/se_execute.hpp index 5b242c597..cc28d3ebb 100644 --- a/libraries/libexosphere/source/se/se_execute.hpp +++ b/libraries/libexosphere/source/se/se_execute.hpp @@ -23,6 +23,7 @@ namespace ams::se { void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size); void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address); void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler); diff --git a/libraries/libexosphere/source/se/se_oaep.cpp b/libraries/libexosphere/source/se/se_oaep.cpp new file mode 100644 index 000000000..debbeec57 --- /dev/null +++ b/libraries/libexosphere/source/se/se_oaep.cpp @@ -0,0 +1,122 @@ +/* + * 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 "se_execute.hpp" + +namespace ams::se { + + /* NOTE: This implementation is mostly copy/pasted from crypto::impl::RsaOaepImpl. */ + + namespace { + + constexpr inline size_t HashSize = sizeof(Sha256Hash); + + constexpr inline u8 HeadMagic = 0x00; + + void ApplyMGF1(u8 *dst, size_t dst_size, const void *src, size_t src_size) { + /* Check our pre-conditions. */ + AMS_ABORT_UNLESS(src_size <= RsaSize - (1 + HashSize)); + + /* Create a buffer. */ + util::AlignedBuffer<hw::DataCacheLineSize, RsaSize - (1 + HashSize) + sizeof(u32)> buf; + u32 counter = 0; + + while (dst_size > 0) { + /* Setup the current hash buffer. */ + const size_t cur_size = std::min(HashSize, dst_size); + std::memcpy(static_cast<u8 *>(buf), src, src_size); + { + u32 counter_be; + util::StoreBigEndian(std::addressof(counter_be), counter++); + std::memcpy(static_cast<u8 *>(buf) + src_size, std::addressof(counter_be), sizeof(counter_be)); + } + + /* Ensure se sees correct data. */ + hw::FlushDataCache(buf, src_size + sizeof(u32)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + Sha256Hash hash; + se::CalculateSha256(std::addressof(hash), buf, src_size + sizeof(u32)); + + /* Mask the current output. */ + const u8 *mask = hash.bytes; + for (size_t i = 0; i < cur_size; ++i) { + *(dst++) ^= *(mask++); + } + + /* Advance. */ + dst_size -= cur_size; + } + } + + } + + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size) { + /* Check our preconditions. */ + AMS_ABORT_UNLESS(src_size == RsaSize); + AMS_ABORT_UNLESS(label_digest_size == HashSize); + + /* Get a byte-readable copy of the input. */ + u8 *buf = static_cast<u8 *>(src); + + /* Validate sanity byte. */ + bool is_valid = buf[0] == HeadMagic; + + /* Decrypt seed and masked db. */ + size_t db_len = src_size - HashSize - 1; + u8 *seed = buf + 1; + u8 *db = seed + HashSize; + ApplyMGF1(seed, HashSize, db, db_len); + ApplyMGF1(db, db_len, seed, HashSize); + + /* Check the label digest. */ + is_valid &= crypto::IsSameBytes(label_digest, db, HashSize); + + /* Skip past the label digest. */ + db += HashSize; + db_len -= HashSize; + + /* Verify that DB is of the form 0000...0001 < message > */ + s32 msg_ofs = 0; + { + int looking_for_one = 1; + int invalid_db_padding = 0; + int is_zero; + int is_one; + for (size_t i = 0; i < db_len; /* ... */) { + is_zero = (db[i] == 0); + is_one = (db[i] == 1); + msg_ofs += (looking_for_one & is_one) * (static_cast<s32>(++i)); + looking_for_one &= ~is_one; + invalid_db_padding |= (looking_for_one & ~is_zero); + } + + is_valid &= (invalid_db_padding == 0); + } + + /* If we're invalid, return zero size. */ + const size_t valid_msg_size = db_len - msg_ofs; + const size_t msg_size = std::min(dst_size, static_cast<size_t>(is_valid) * valid_msg_size); + + /* Copy to output. */ + std::memcpy(dst, db + msg_ofs, msg_size); + + /* Return copied size. */ + return msg_size; + } + +} diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp index 99ec5e90a..9fc473931 100644 --- a/libraries/libexosphere/source/se/se_rsa.cpp +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -67,6 +67,10 @@ namespace ams::se { } } + void WaitForInputReadComplete(volatile SecurityEngineRegisters *SE) { + while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_IN_DONE, CLEAR))) { /* ... */ } + } + } void ClearRsaKeySlot(int slot) { @@ -174,4 +178,53 @@ namespace ams::se { GetRsaResult(SE, dst, dst_size); } + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler) { + /* Validate the slot and size. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(src_size <= RsaSize); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Create a work buffer. */ + u8 work[RsaSize]; + util::ClearMemory(work, sizeof(work)); + + /* Copy the input into the work buffer (reversing endianness). */ + const u8 *src_u8 = static_cast<const u8 *>(src); + for (size_t i = 0; i < src_size; ++i) { + work[src_size - 1 - i] = src_u8[i]; + } + + /* Flush the work buffer to ensure the SE sees correct results. */ + hw::FlushDataCache(work, sizeof(work)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the engine to perform RSA encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG)); + + /* Configure the engine to use the keyslot and correct modulus/exp sizes. */ + const auto &info = g_rsa_key_infos[slot]; + reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot)); + reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val); + reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val); + + /* Set the done handler. */ + SetDoneHandler(SE, handler); + + /* Trigger the input operation. */ + StartInputOperation(SE, work, src_size); + + /* Wait for input to be read by the se. */ + WaitForInputReadComplete(SE); + } + + void GetRsaResult(void *dst, size_t dst_size) { + GetRsaResult(GetRegisters(), dst, dst_size); + } + } From 8a4019151b9c124597bdc7f3e3e4257ea83bb2e0 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Wed, 20 May 2020 08:10:54 -0700 Subject: [PATCH 078/118] exo2: implement SmcModularExponentiate(ByStorageKey) --- .../program/source/secmon_key_storage.cpp | 6 + .../program/source/secmon_key_storage.hpp | 1 + .../program/source/smc/secmon_smc_rsa.cpp | 193 +++++++++++++++++- 3 files changed, 190 insertions(+), 10 deletions(-) diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp index 419b82cdb..207f10264 100644 --- a/exosphere2/program/source/secmon_key_storage.cpp +++ b/exosphere2/program/source/secmon_key_storage.cpp @@ -20,6 +20,8 @@ namespace ams::secmon { namespace { + constexpr const u8 RsaPublicKey[] = { 0x00, 0x01, 0x00, 0x01 }; + constinit u8 g_rsa_moduli[ImportRsaKey_Count][se::RsaSize] = {}; constinit bool g_rsa_modulus_committed[ImportRsaKey_Count] = {}; @@ -80,6 +82,10 @@ namespace ams::secmon { se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize); } + void LoadProvisionalRsaPublicKey(int slot, ImportRsaKey which) { + se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, RsaPublicKey, sizeof(RsaPublicKey)); + } + void SetMasterKey(int generation, const void *src, size_t size) { const int index = generation - pkg1::KeyGeneration_Min; se::EncryptAes128(GetMasterKeyStorage(index), se::AesBlockSize, pkg1::AesKeySlot_RandomForKeyStorageWrap, src, size); diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp index 703ae53a7..076756dae 100644 --- a/exosphere2/program/source/secmon_key_storage.hpp +++ b/exosphere2/program/source/secmon_key_storage.hpp @@ -36,6 +36,7 @@ namespace ams::secmon { bool LoadRsaKey(int slot, ImportRsaKey which); void LoadProvisionalRsaKey(int slot, ImportRsaKey which); + void LoadProvisionalRsaPublicKey(int slot, ImportRsaKey which); void SetMasterKey(int generation, const void *src, size_t size); void LoadMasterKey(int slot, int generation); diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp index aa1e33165..20b44711d 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -25,12 +25,24 @@ namespace ams::secmon::smc { namespace { + struct ModularExponentiateByStorageKeyOption { + using Mode = util::BitPack32::Field<0, 2, u32>; + using Reserved = util::BitPack32::Field<2, 30, u32>; + }; + struct PrepareEsDeviceUniqueKeyOption { using KeyGeneration = util::BitPack32::Field<0, 6, int>; using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>; using Reserved = util::BitPack32::Field<7, 25, u32>; }; + constexpr const u8 ModularExponentiateByStorageKeyTable[] = { + static_cast<u8>(ImportRsaKey_Lotus), + static_cast<u8>(ImportRsaKey_Ssl), + static_cast<u8>(ImportRsaKey_EsClientCert), + }; + constexpr size_t ModularExponentiateByStorageKeyTableSize = util::size(ModularExponentiateByStorageKeyTable); + class PrepareEsDeviceUniqueKeyAsyncArguments { private: int generation; @@ -56,10 +68,14 @@ namespace ams::secmon::smc { std::memcpy(this->msg, m, sizeof(this->msg)); } - void GetMessage(void *dst, size_t dst_size) const { std::memcpy(dst, this->msg, sizeof(this->msg)); } + const u8 *GetMessage() const { return this->msg; } }; - constinit bool g_exp_mod_completed = false; + constinit SmcResult g_exp_mod_result = SmcResult::Success; + + constinit bool g_test_exp_mod_public = false; + constinit int g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; + constinit ImportRsaKey g_test_exp_mod_key = {}; constinit union { ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key; @@ -76,10 +92,155 @@ namespace ams::secmon::smc { void SecurityEngineDoneHandler() { /* End the asynchronous operation. */ - g_exp_mod_completed = true; + g_exp_mod_result = SmcResult::Success; EndAsyncOperation(); } + void TestRsaPublicKey(ImportRsaKey which, int slot, const void *mod, size_t mod_size, se::DoneHandler handler) { + /* Declare a buffer for our test message. */ + u8 msg[se::RsaSize]; + std::memset(msg, 'D', sizeof(msg)); + + /* Provisionally import the modulus. */ + ImportRsaKeyModulusProvisionally(which, mod, mod_size); + + /* Load the provisional public key into the slot. */ + LoadProvisionalRsaPublicKey(slot, which); + + /* Perform the test exponentiation. */ + se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); + } + + void TestRsaPrivateKey(ImportRsaKey which, int slot, se::DoneHandler handler) { + /* Get the result of the public key test. */ + u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Load the provisional private key into the slot. */ + LoadProvisionalRsaKey(slot, which); + + /* Perform the test exponentiation. */ + se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); + } + + void VerifyTestRsaKeyResult(ImportRsaKey which) { + /* Get the result of the test. */ + u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Validate the result. */ + const bool is_valid = (msg[0] == 'D') & (crypto::IsSameBytes(msg, msg + 1, sizeof(msg) - 1)); + + /* If the test passes, the key is no longer provisional. */ + if (is_valid) { + CommitRsaKeyModulus(which); + } + } + + void TestRsaKeyDoneHandler() { + if (g_test_exp_mod_public) { + /* If we're testing the public key, we still have another exponentiation to do to test the private key. */ + g_test_exp_mod_public = false; + + /* Test the private key. */ + TestRsaPrivateKey(g_test_exp_mod_key, g_test_exp_mod_slot, TestRsaKeyDoneHandler); + } else { + /* We're testing the private key, so validate the result. */ + VerifyTestRsaKeyResult(g_test_exp_mod_key); + + /* If the test passed, we can proceed to perform the intended exponentiation. */ + if (LoadRsaKey(g_test_exp_mod_slot, g_test_exp_mod_key)) { + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, GetModularExponentiateByStorageKeyAsyncArguments().GetMessage(), se::RsaSize, SecurityEngineDoneHandler); + } else { + /* The test failed, so end the asynchronous operation. */ + g_exp_mod_result = SmcResult::InvalidArgument; + EndAsyncOperation(); + } + } + } + + SmcResult ModularExponentiateImpl(SmcArguments &args) { + /* Decode arguments. */ + const uintptr_t msg_address = args.r[1]; + const uintptr_t exp_address = args.r[2]; + const uintptr_t mod_address = args.r[3]; + const size_t exp_size = args.r[4]; + + /* Validate arguments. */ + SMC_R_UNLESS(util::IsAligned(exp_size, sizeof(u32)), InvalidArgument); + SMC_R_UNLESS(exp_size <= se::RsaSize, InvalidArgument); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 exp[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(exp, exp_address, exp_size), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; + + /* Load the key into the temporary keyslot. */ + se::SetRsaKey(pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), exp, exp_size); + + /* Begin the asynchronous exponentiation. */ + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + + return SmcResult::Success; + } + + SmcResult ModularExponentiateByStorageKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + const uintptr_t msg_address = args.r[1]; + const uintptr_t mod_address = args.r[2]; + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + + const auto mode = option.Get<ModularExponentiateByStorageKeyOption::Mode>(); + const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_UNLESS(mode < ModularExponentiateByStorageKeyTableSize, InvalidArgument); + + /* Convert the mode to an import key. */ + const auto import_key = static_cast<ImportRsaKey>(ModularExponentiateByStorageKeyTable[mode]); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; + + /* In the ideal case, the key pair is already verified. If it is, we can use it directly. */ + if (LoadRsaKey(pkg1::RsaKeySlot_Temporary, import_key)) { + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + } else { + /* Set the async arguments. */ + GetModularExponentiateByStorageKeyAsyncArguments().Set(msg, sizeof(msg)); + + /* Test the rsa key. */ + g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; + g_test_exp_mod_key = import_key; + g_test_exp_mod_public = true; + + TestRsaPublicKey(import_key, pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), TestRsaKeyDoneHandler); + } + + return SmcResult::Success; + } + SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) { /* Decode arguments. */ u8 label_digest[crypto::Sha256Generator::HashSize]; @@ -109,8 +270,8 @@ namespace ams::secmon::smc { SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); } - /* We're performing an operation, so the operation is not completed. */ - g_exp_mod_completed = false; + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; /* Set the async arguments. */ GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest); @@ -124,6 +285,20 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult GetModularExponentiateResult(void *dst, size_t dst_size) { + /* Validate state. */ + SMC_R_TRY(g_exp_mod_result); + SMC_R_UNLESS(dst_size == se::RsaSize, InvalidArgument); + + /* We want to relinquish our security engine lock at the end of scope. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Get the result of the exponentiation. */ + se::GetRsaResult(dst, se::RsaSize); + + return SmcResult::Success; + } + SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) { /* Declare variables. */ u8 key_source[se::AesBlockSize]; @@ -131,7 +306,7 @@ namespace ams::secmon::smc { u8 access_key[se::AesBlockSize]; /* Validate state. */ - SMC_R_UNLESS(g_exp_mod_completed, Busy); + SMC_R_TRY(g_exp_mod_result); SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument); /* We want to relinquish our security engine lock at the end of scope. */ @@ -168,13 +343,11 @@ namespace ams::secmon::smc { } SmcResult SmcModularExponentiate(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateImpl, GetModularExponentiateResult); } SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateByStorageKeyImpl, GetModularExponentiateResult); } SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { From 8e401f4daaf246bd64ff82373a6658264abbeb0c Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 25 May 2020 22:32:54 -0700 Subject: [PATCH 079/118] exo2: implement smc cpu off --- exosphere2/program/source/secmon_cache.inc | 1 + .../program/source/secmon_cache_impl.inc | 40 ++++++++ .../program/source/secmon_start_virtual.s | 14 +++ .../program/source/smc/secmon_smc_cpu_asm.s | 94 +++++++++++++++++++ .../program/source/smc/secmon_smc_handler.cpp | 11 ++- .../program/source/smc/secmon_smc_info.cpp | 22 ++++- .../smc/secmon_smc_power_management.cpp | 49 +++++++++- .../libexosphere/include/exosphere/flow.hpp | 4 + .../exosphere/tegra/tegra_flow_ctlr.hpp | 13 +++ .../libexosphere/source/flow/flow_api.cpp | 30 +++++- 10 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 exosphere2/program/source/smc/secmon_smc_cpu_asm.s diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc index ea57c0f4e..8ae241ae4 100644 --- a/exosphere2/program/source/secmon_cache.inc +++ b/exosphere2/program/source/secmon_cache.inc @@ -15,6 +15,7 @@ */ void FlushEntireDataCache(); +void FlushEntireDataCacheLocal(); void InvalidateEntireDataCache(); void EnsureMappingConsistency(); diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc index 6ef2fe4bc..a305ce698 100644 --- a/exosphere2/program/source/secmon_cache_impl.inc +++ b/exosphere2/program/source/secmon_cache_impl.inc @@ -59,6 +59,40 @@ namespace { } } + void FlushDataCacheFrom(int loc) { + for (int level = loc - 1; level >= 0; --level) { + /* Set the selection register. */ + { + util::BitPack32 csselr = {}; + csselr.Set<hw::CsselrEl1::InD>(0); + csselr.Set<hw::CsselrEl1::Level>(level); + HW_CPU_SET_CSSELR_EL1(csselr); + } + + /* Ensure that reordering doesn't occur around this operation. */ + hw::InstructionSynchronizationBarrier(); + + /* Get ccsidr. */ + util::BitPack32 ccsidr; + HW_CPU_GET_CCSIDR_EL1(ccsidr); + + /* Get cache size id info. */ + const int num_sets = ccsidr.Get<hw::CcsidrEl1::NumSets>() + 1; + const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1; + const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 4; + + const int way_shift = 32 - FloorLog2(num_ways); + const int set_shift = line_size; + + for (int way = 0; way <= num_ways; way++) { + for (int set = 0; set <= num_sets; set++) { + const u64 value = (static_cast<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(level) << 1); + __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory"); + } + } + } + } + void InvalidateDataCacheTo(int loc) { for (int level = 0; level < loc; ++level) { /* Set the selection register. */ @@ -101,6 +135,12 @@ void FlushEntireDataCache() { FlushDataCacheTo(clidr.Get<hw::ClidrEl1::Loc>()); } +void FlushEntireDataCacheLocal() { + util::BitPack32 clidr; + HW_CPU_GET_CLIDR_EL1(clidr); + FlushDataCacheFrom(clidr.Get<hw::ClidrEl1::Louis>()); +} + void InvalidateEntireDataCache() { util::BitPack32 clidr; HW_CPU_GET_CLIDR_EL1(clidr); diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 8d23a66df..9a7a03488 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -191,6 +191,20 @@ _ZN3ams6secmon26ReleaseCommonWarmbootStackEv: /* Return. */ ret +.section .text._ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE, "ax", %progbits +.align 4 +.global _ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE +_ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE: + /* Pivot to use the provided stack pointer. */ + mov sp, x0 + + /* Release our lock on the common smc stack. */ + mov x19, x1 + bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv + + /* Invoke the function with the new stack. */ + br x19 + .section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits .global _ZN3ams6secmon18CommonSmcStackLockE _ZN3ams6secmon18CommonSmcStackLockE: diff --git a/exosphere2/program/source/smc/secmon_smc_cpu_asm.s b/exosphere2/program/source/smc/secmon_smc_cpu_asm.s new file mode 100644 index 000000000..dd9f4dfc8 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_cpu_asm.s @@ -0,0 +1,94 @@ +/* + * 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 + +.section .text._ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE, "ax", %progbits +.align 4 +.global _ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE +_ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE: + /* Pivot to use the provided stack pointer. */ + mov sp, x0 + + /* Release our lock on the common smc stack. */ + mov x19, x1 + bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv + + /* Invoke the function with the new stack. */ + br x19 + +.section .text._ZN3ams6secmon3smc16FinalizePowerOffEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon3smc16FinalizePowerOffEv +_ZN3ams6secmon3smc16FinalizePowerOffEv: + /* Disable all caches by clearing sctlr_el1.C. */ + mrs x0, sctlr_el1 + and x0, x0, #~(1 << 2) + msr sctlr_el1, x0 + isb + + /* Disable all caches by clearing sctlr_el3.C. */ + mrs x0, sctlr_el3 + and x0, x0, #~(1 << 2) + msr sctlr_el3, x0 + isb + + /* Disable prefetching of page table walking descriptors. */ + mrs x0, cpuectlr_el1 + orr x0, x0, #(1 << 38) + + /* Disable prefetching of instructions. */ + and x0, x0, #~(3 << 35) + + /* Disable prefetching of data. */ + and x0, x0, #~(3 << 32) + msr cpuectlr_el1, x0 + isb + + /* Ensure that all data prefetching prior to our configuration change completes. */ + dsb sy + + /* Flush the entire data cache (local). */ + bl _ZN3ams6secmon25FlushEntireDataCacheLocalEv + + /* Disable receiving instruction cache/TLB maintenance operations. */ + mrs x0, cpuectlr_el1 + and x0, x0, #~(1 << 6) + msr cpuectlr_el1, x0 + + /* Configure the gic to not send interrupts to the current core. */ + ldr x1, =0x1F0043000 + mov w0, #0x1E0 /* Set FIQBypDisGrp1, IRQBypDisGrp1, reserved bits 7/8. */ + str w0, [x1] + + /* Lock the OS Double Lock. */ + mrs x0, osdlr_el1 + orr x0, x0, #(1 << 0) + msr osdlr_el1, x0 + + /* Ensure that our configuration takes. */ + isb + dsb sy + + /* Wait for interrupts, infinitely. */ +0: + wfi + b 0b + + + diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index c2577806c..bc044b021 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -248,7 +248,6 @@ namespace ams::secmon::smc { return; } - const int ind = current - LogMin; const int ofs = (ind * sizeof(args)) % LogBufSize; @@ -275,6 +274,16 @@ namespace ams::secmon::smc { DebugLog(args); } + if (args.r[0] == 0xC4000001) { + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xFFFFFFFF; + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(hw::GetCurrentCoreId()); + + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } + /* Get the handler info. */ const auto &info = GetHandlerInfo(table, args.r[0]); diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index c94bd7d1d..c36ef37b2 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -268,6 +268,25 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult SetConfig(SmcArguments &args) { + switch (static_cast<ConfigItem>(args.r[1])) { + case ConfigItem::IsChargerHiZModeEnabled: + /* Configure the HiZ mode. */ + SetChargerHiZModeEnabled(static_cast<bool>(args.r[3])); + break; + case ConfigItem::ExosphereNeedsReboot: + /* TODO */ + return SmcResult::NotImplemented; + case ConfigItem::ExosphereNeedsShutdown: + /* TODO */ + return SmcResult::NotImplemented; + default: + return SmcResult::InvalidArgument; + } + + return SmcResult::Success; + } + } SmcResult SmcGetConfigUser(SmcArguments &args) { @@ -279,8 +298,7 @@ namespace ams::secmon::smc { } SmcResult SmcSetConfig(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return SetConfig(args); } /* This is an atmosphere extension smc. */ diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 92cbd3491..3af573a75 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -14,12 +14,24 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> +#include "../secmon_cache.hpp" #include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" #include "secmon_smc_power_management.hpp" +namespace ams::secmon { + + /* Declare assembly functionality. */ + void *GetCoreExceptionStackVirtual(); + +} + namespace ams::secmon::smc { + /* Declare assembly power-management functionality. */ + void PivotStackAndInvoke(void *stack, void (*function)()); + void FinalizePowerOff(); + namespace { constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); @@ -94,11 +106,44 @@ namespace ams::secmon::smc { REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ } + void PowerOffCpu() { + /* Get the current core id. */ + const auto core_id = hw::GetCurrentCoreId(); + + /* Configure the flow controller to prepare for shutting down the current core. */ + flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_DISABLE); + flow::SetHaltCpuEvents(core_id, false); + flow::SetCc4Ctrl(core_id, 0); + + /* Save the core's context for restoration on next power-on. */ + SaveDebugRegisters(); + SetCoreOff(); + + /* Ensure there are no pending memory transactions prior to our power-down. */ + FlushEntireDataCache(); + + /* Finalize our powerdown and wait for an interrupt. */ + FinalizePowerOff(); + } + } SmcResult SmcPowerOffCpu(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Get the current core id. */ + const auto core_id = hw::GetCurrentCoreId(); + + /* Note that we're expecting a reset for the current core. */ + SetResetExpected(true); + + /* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */ + if (core_id == NumCores - 1) { + PowerOffCpu(); + } else { + PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpu); + } + + /* This code will never be reached. */ + __builtin_unreachable(); } SmcResult SmcPowerOnCpu(SmcArguments &args) { diff --git a/libraries/libexosphere/include/exosphere/flow.hpp b/libraries/libexosphere/include/exosphere/flow.hpp index 93cfb98c3..210ecceda 100644 --- a/libraries/libexosphere/include/exosphere/flow.hpp +++ b/libraries/libexosphere/include/exosphere/flow.hpp @@ -22,4 +22,8 @@ namespace ams::flow { void ResetCpuRegisters(int core); + void SetCpuCsr(int core, u32 enable_ext); + void SetHaltCpuEvents(int core, bool resume_on_irq); + void SetCc4Ctrl(int core, u32 value); + } diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index d30493330..7c2f78919 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -47,6 +47,19 @@ #define DEFINE_FLOW_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) #define DEFINE_FLOW_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) +DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_ENABLE, 0, DISABLE, ENABLE); +DEFINE_FLOW_REG(CPUN_CSR_WAIT_WFI_BITMAP, 8, 4); +DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_ENABLE_EXT, 12, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_EVENT_FLAG, 14, FALSE, TRUE); +DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_INTR_FLAG, 15, FALSE, TRUE); + +DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_GIC_FIQN, 8, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_GIC_IRQN, 9, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_FIQN, 10, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_IRQN, 11, DISABLE, ENABLE); + +DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, 29, NONE, RUN_AND_INT, WAITEVENT, WAITEVENT_AND_INT, STOP_UNTIL_IRQ, STOP_UNTIL_EVENT_AND_IRQ, RESERVED6, RESERVED7); + DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, ENABLED, DISABLED); DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MODE_RUN_AND_INT, FLOW_MODE_STOP, FLOW_MODE_STOP_AND_INT, FLOW_MODE_STOP_UNTIL_IRQ, FLOW_MODE_STOP_UNTIL_IRQ_AND_INT, FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ, RESERVED7); diff --git a/libraries/libexosphere/source/flow/flow_api.cpp b/libraries/libexosphere/source/flow/flow_api.cpp index 613c78c07..82a19943b 100644 --- a/libraries/libexosphere/source/flow/flow_api.cpp +++ b/libraries/libexosphere/source/flow/flow_api.cpp @@ -34,6 +34,18 @@ namespace ams::flow { { FLOW_CTLR_CPU3_CSR, FLOW_CTLR_HALT_CPU3_EVENTS, FLOW_CTLR_CC4_CORE3_CTRL, }, }; + constexpr u32 GetHaltCpuEventsValue(bool resume_on_irq) { + if (resume_on_irq) { + return reg::Encode(FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, WAITEVENT), + FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_LIC_IRQN, ENABLE), + FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_LIC_FIQN, ENABLE), + FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_GIC_IRQN, ENABLE), + FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_GIC_FIQN, ENABLE)); + } else { + return reg::Encode(FLOW_REG_BITS_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, WAITEVENT)); + } + } + } void SetRegisterAddress(uintptr_t address) { @@ -48,4 +60,20 @@ namespace ams::flow { reg::Write(g_register_address + offsets.halt_cpu_events, 0); } -} \ No newline at end of file + void SetCpuCsr(int core, u32 enable_ext) { + reg::Write(g_register_address + FlowControllerRegisterOffsets[core].cpu_csr, FLOW_REG_BITS_ENUM (CPUN_CSR_INTR_FLAG, TRUE), + FLOW_REG_BITS_ENUM (CPUN_CSR_EVENT_FLAG, TRUE), + FLOW_REG_BITS_VALUE(CPUN_CSR_ENABLE_EXT, enable_ext), + FLOW_REG_BITS_VALUE(CPUN_CSR_WAIT_WFI_BITMAP, (1u << core)), + FLOW_REG_BITS_ENUM (CPUN_CSR_ENABLE, ENABLE)); + } + + void SetHaltCpuEvents(int core, bool resume_on_irq) { + reg::Write(g_register_address + FlowControllerRegisterOffsets[core].halt_cpu_events, GetHaltCpuEventsValue(resume_on_irq)); + } + + void SetCc4Ctrl(int core, u32 value) { + reg::Write(g_register_address + FlowControllerRegisterOffsets[core].cc4_core_ctrl, value); + } + +} From 068c25ce66e14d0b60eeaaf89449b4e8068c2638 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 5 Jun 2020 04:07:56 -0700 Subject: [PATCH 080/118] exo2: add a number of minor configuration fixes --- exosphere2/program/source/secmon_setup.cpp | 8 ++++++-- .../source/smc/secmon_smc_power_management.cpp | 2 +- .../include/exosphere/tegra/tegra_ahb_arbc.hpp | 12 ++++++++++++ .../include/exosphere/tegra/tegra_flow_ctlr.hpp | 2 +- libraries/libexosphere/source/gic/gic_api.cpp | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index f2ae08b69..a6a9ce914 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -29,6 +29,7 @@ namespace ams::secmon { namespace { constexpr inline const uintptr_t TIMER = secmon::MemoryRegionVirtualDeviceTimer.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionVirtualDeviceSystem.GetAddress(); constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress(); constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionVirtualDeviceFlowController.GetAddress(); constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc.GetAddress(); @@ -784,12 +785,12 @@ namespace ams::secmon { reg::Write(MC + MC_IRAM_TOM, ( 0u) & MC_IRAM_TOM_WRITE_MASK); /* Lock the IRAM aperture. */ - reg::Write(MC + MC_IRAM_REG_CTRL, MC_REG_BITS_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, DISABLED)); + reg::ReadWrite(MC + MC_IRAM_REG_CTRL, MC_REG_BITS_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, DISABLED)); /* Disable the ARC clock gate override. */ reg::ReadWrite(CLK_RST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, CLK_RST_REG_BITS_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, OFF)); - /* Rea IRAM REG CTRL to make sure our writes take. */ + /* Read IRAM REG CTRL to make sure our writes take. */ reg::Read(MC + MC_IRAM_REG_CTRL); } @@ -1126,6 +1127,9 @@ namespace ams::secmon { reg::Write(EVP + EVP_COP_IRQ_VECTOR, BpmpExceptionVector); reg::Write(EVP + EVP_COP_FIQ_VECTOR, BpmpExceptionVector); + /* Disable arbitration for the bpmp. */ + reg::ReadWrite(SYSTEM + AHB_ARBITRATION_DISABLE, AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_COP, DISABLE)); + /* Turn on the SMMU for the BPMP. */ EnableBpmpSmmu(); diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 3af573a75..3d70fbc16 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -111,7 +111,7 @@ namespace ams::secmon::smc { const auto core_id = hw::GetCurrentCoreId(); /* Configure the flow controller to prepare for shutting down the current core. */ - flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_DISABLE); + flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_ONLY); flow::SetHaltCpuEvents(core_id, false); flow::SetCc4Ctrl(core_id, 0); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp index a131cf68d..74bf76ee6 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp @@ -24,3 +24,15 @@ #define AHB_MASTER_SWID_1 (0x038) #define AHB_GIZMO_TZRAM (0x054) +#define AHB_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AHB_, NAME) +#define AHB_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AHB_, NAME, VALUE) +#define AHB_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AHB_, NAME, ENUM) +#define AHB_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AHB_, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_AHB_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AHB_, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_AHB_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_AHB_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_AHB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_AHB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_COP, 1, ENABLE, DISABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index 7c2f78919..d9077a9a3 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -49,7 +49,7 @@ DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_ENABLE, 0, DISABLE, ENABLE); DEFINE_FLOW_REG(CPUN_CSR_WAIT_WFI_BITMAP, 8, 4); -DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_ENABLE_EXT, 12, DISABLE, ENABLE); +DEFINE_FLOW_REG_TWO_BIT_ENUM(CPUN_CSR_ENABLE_EXT, 12, POWERGATE_CPU_ONLY, POWERGATE_BOTH_CPU_NONCPU, POWERGATE_CPU_TURNOFF_CPURAIL, PG_EMULATION); DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_EVENT_FLAG, 14, FALSE, TRUE); DEFINE_FLOW_REG_BIT_ENUM(CPUN_CSR_INTR_FLAG, 15, FALSE, TRUE); diff --git a/libraries/libexosphere/source/gic/gic_api.cpp b/libraries/libexosphere/source/gic/gic_api.cpp index 220713e79..f04b46568 100644 --- a/libraries/libexosphere/source/gic/gic_api.cpp +++ b/libraries/libexosphere/source/gic/gic_api.cpp @@ -124,7 +124,11 @@ namespace ams::gic { const int word = i / scale; const int bit = (i % scale) * width; - reg::ReadWrite(address + sizeof(u32) * word, REG_BITS_VALUE(bit, width, value)); + const u32 mask = ((1u << width) - 1) << bit; + + const uintptr_t reg_addr = address + sizeof(u32) * word; + const u32 old = reg::Read(reg_addr) & ~mask; + reg::Write(reg_addr, old | ((value << bit) & mask)); } void Write(uintptr_t address, int width, int i, u32 value) { From e1835d9ba27bf685b2b1b71a325341b8ccf25254 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 7 Jun 2020 16:46:17 -0700 Subject: [PATCH 081/118] exo2: correct exception stack calc (fixes smc cpu off) --- exosphere2/program/source/secmon_start_virtual.s | 2 +- exosphere2/program/source/smc/secmon_smc_handler.cpp | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s index 9a7a03488..d8b135644 100644 --- a/exosphere2/program/source/secmon_start_virtual.s +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -122,7 +122,7 @@ _ZN3ams6secmon28GetCoreExceptionStackVirtualEv: cmp x0, #2 b.eq 2f - cmp x1, #1 + cmp x0, #1 b.eq 1f /* cmp x0, #0 */ diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index bc044b021..644139b3c 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -274,16 +274,6 @@ namespace ams::secmon::smc { DebugLog(args); } - if (args.r[0] == 0xC4000001) { - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xFFFFFFFF; - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(hw::GetCurrentCoreId()); - - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } - /* Get the handler info. */ const auto &info = GetHandlerInfo(table, args.r[0]); From 0202a95832de42d0bbafa4a63b42f983803a739b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 7 Jun 2020 19:16:48 -0700 Subject: [PATCH 082/118] exo2: implement the first half of SmcCpuSuspend --- .../smc/secmon_smc_power_management.cpp | 112 +++++++++++++++++- .../libexosphere/include/exosphere/clkrst.hpp | 1 + .../libexosphere/include/exosphere/flow.hpp | 1 + .../libexosphere/include/exosphere/pmc.hpp | 2 + .../libexosphere/include/exosphere/pmic.hpp | 2 + .../exosphere/tegra/tegra_flow_ctlr.hpp | 1 + .../include/exosphere/tegra/tegra_pmc.hpp | 2 + .../libexosphere/source/clkrst/clkrst_api.cpp | 2 +- .../libexosphere/source/flow/flow_api.cpp | 4 + libraries/libexosphere/source/pmc/pmc_api.cpp | 26 ++++ .../libexosphere/source/pmic/pmic_api.cpp | 27 ++++- 11 files changed, 175 insertions(+), 5 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 3d70fbc16..7596903cb 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -18,6 +18,7 @@ #include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" #include "secmon_smc_power_management.hpp" +#include "secmon_smc_se_lock.hpp" namespace ams::secmon { @@ -35,8 +36,27 @@ namespace ams::secmon::smc { namespace { constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress(); constexpr inline uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1)); + + enum PowerStateType { + PowerStateType_StandBy = 0, + PowerStateType_PowerDown = 1, + }; + + enum PowerStateId { + PowerStateId_Sc7 = 27, + }; + + /* http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf Page 46 */ + struct SuspendCpuPowerState { + using StateId = util::BitPack32::Field< 0, 16, PowerStateId>; + using StateType = util::BitPack32::Field<16, 1, PowerStateType>; + using PowerLevel = util::BitPack32::Field<24, 2, u32>; + }; + constinit bool g_charger_hi_z_mode_enabled = false; constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = { @@ -126,6 +146,95 @@ namespace ams::secmon::smc { FinalizePowerOff(); } + void ValidateSocStateForSuspend() { + /* TODO */ + } + + void SaveSecureContextAndSuspend() { + /* TODO */ + + /* Finalize our powerdown and wait for an interrupt. */ + FinalizePowerOff(); + } + + SmcResult SuspendCpuImpl(SmcArguments &args) { + /* Decode arguments. */ + const util::BitPack32 power_state = { static_cast<u32>(args.r[1]) }; + const uintptr_t entry_point = args.r[2]; + const uintptr_t context_id = args.r[3]; + + const auto state_type = power_state.Get<SuspendCpuPowerState::StateType>(); + const auto state_id = power_state.Get<SuspendCpuPowerState::StateId>(); + + const auto core_id = hw::GetCurrentCoreId(); + + /* Validate arguments. */ + SMC_R_UNLESS(state_type == PowerStateType_PowerDown, PsciDenied); + SMC_R_UNLESS(state_id == PowerStateId_Sc7, PsciDenied); + + /* Orchestrate charger transition to Hi-Z mode if needed. */ + if (IsChargerHiZModeEnabled()) { + /* Ensure we can do comms over i2c-1. */ + clkrst::EnableI2c1Clock(); + + /* If the charger isn't in hi-z mode, perform a transition. */ + if (!charger::IsHiZMode()) { + charger::EnterHiZMode(); + + /* Wait up to 50ms for the transition to complete. */ + const auto start_time = util::GetMicroSeconds(); + auto current_time = start_time; + while ((current_time - start_time) <= 50'000) { + if (auto intr_status = reg::Read(GPIO + 0x634); (intr_status & 1) == 0) { + /* Wait 256 us to ensure the transition completes. */ + util::WaitMicroSeconds(256); + break; + } + current_time = util::GetMicroSeconds(); + } + } + + /* Disable i2c-1, since we're done communicating over it. */ + clkrst::DisableI2c1Clock(); + } + + /* Enable wake event detection. */ + pmc::EnableWakeEventDetection(); + + /* Ensure that i2c-5 is usable for communicating with the pmic. */ + clkrst::EnableI2c5Clock(); + i2c::Initialize(i2c::Port_5); + + /* Orchestrate sleep entry with the pmic. */ + pmic::EnableSleep(); + + /* Ensure that the soc is in a state valid for us to suspend. */ + ValidateSocStateForSuspend(); + + /* Configure the pmc for sc7 entry. */ + pmc::ConfigureForSc7Entry(); + + /* Configure the flow controller for sc7 entry. */ + flow::SetCc4Ctrl(core_id, 0); + flow::SetHaltCpuEvents(core_id, false); + flow::ClearL2FlushControl(); + flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_TURNOFF_CPURAIL); + + /* Save the entry context. */ + SetEntryContext(core_id, entry_point, context_id); + + /* Configure the cpu context for reset. */ + SaveDebugRegisters(); + SetCoreOff(); + SetResetExpected(true); + + /* Switch to use the common smc stack (all other cores are off), and perform suspension. */ + PivotStackAndInvoke(reinterpret_cast<void *>(CommonSmcStackTop), SaveSecureContextAndSuspend); + + /* This code will never be reached. */ + __builtin_unreachable(); + } + } SmcResult SmcPowerOffCpu(SmcArguments &args) { @@ -170,8 +279,7 @@ namespace ams::secmon::smc { } SmcResult SmcSuspendCpu(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, SuspendCpuImpl); } bool IsChargerHiZModeEnabled() { diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp index e4b31740d..2ee681490 100644 --- a/libraries/libexosphere/include/exosphere/clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -27,6 +27,7 @@ namespace ams::clkrst { void EnableUartCClock(); void EnableActmonClock(); void EnableI2c1Clock(); + void EnableI2c5Clock(); void DisableI2c1Clock(); diff --git a/libraries/libexosphere/include/exosphere/flow.hpp b/libraries/libexosphere/include/exosphere/flow.hpp index 210ecceda..b539a29f9 100644 --- a/libraries/libexosphere/include/exosphere/flow.hpp +++ b/libraries/libexosphere/include/exosphere/flow.hpp @@ -25,5 +25,6 @@ namespace ams::flow { void SetCpuCsr(int core, u32 enable_ext); void SetHaltCpuEvents(int core, bool resume_on_irq); void SetCc4Ctrl(int core, u32 value); + void ClearL2FlushControl(); } diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp index d1e2091b4..207307ebc 100644 --- a/libraries/libexosphere/include/exosphere/pmc.hpp +++ b/libraries/libexosphere/include/exosphere/pmc.hpp @@ -36,6 +36,8 @@ namespace ams::pmc { void SetRegisterAddress(uintptr_t address); void InitializeRandomScratch(); + void EnableWakeEventDetection(); + void ConfigureForSc7Entry(); void LockSecureRegister(SecureRegister reg); diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp index 46b10372b..1d01eb811 100644 --- a/libraries/libexosphere/include/exosphere/pmic.hpp +++ b/libraries/libexosphere/include/exosphere/pmic.hpp @@ -29,5 +29,7 @@ namespace ams::pmic { void EnableVddCpu(Regulator regulator); void DisableVddCpu(Regulator regulator); + void EnableSleep(); + bool IsAcOk(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index d9077a9a3..8bc203ff3 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -18,6 +18,7 @@ #define FLOW_CTLR_FLOW_DBG_QUAL (0x050) +#define FLOW_CTLR_L2FLUSH_CONTROL (0x094) #define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098) #define FLOW_CTLR_CPU0_CSR (0x008) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index 3f1d90eda..c6ede5f01 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -212,3 +212,5 @@ DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBB, 21, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBC, 22, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VIC, 23, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_IRAM, 24, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(CNTRL2_WAKE_DET_EN, 9, DISABLE, ENABLE); diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp index c9ac4f983..c0ae50b25 100644 --- a/libraries/libexosphere/source/clkrst/clkrst_api.cpp +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -105,7 +105,7 @@ namespace ams::clkrst { } void EnableI2c5Clock() { - EnableClock(I2c1Clock); + EnableClock(I2c5Clock); } void DisableI2c1Clock() { diff --git a/libraries/libexosphere/source/flow/flow_api.cpp b/libraries/libexosphere/source/flow/flow_api.cpp index 82a19943b..62d00640a 100644 --- a/libraries/libexosphere/source/flow/flow_api.cpp +++ b/libraries/libexosphere/source/flow/flow_api.cpp @@ -76,4 +76,8 @@ namespace ams::flow { reg::Write(g_register_address + FlowControllerRegisterOffsets[core].cc4_core_ctrl, value); } + void ClearL2FlushControl() { + reg::Write(g_register_address + FLOW_CTLR_L2FLUSH_CONTROL, 0); + } + } diff --git a/libraries/libexosphere/source/pmc/pmc_api.cpp b/libraries/libexosphere/source/pmc/pmc_api.cpp index 67739e583..3de6b17a4 100644 --- a/libraries/libexosphere/source/pmc/pmc_api.cpp +++ b/libraries/libexosphere/source/pmc/pmc_api.cpp @@ -224,6 +224,32 @@ namespace ams::pmc { LockSecureRegister(SecureRegister_Srk); } + void EnableWakeEventDetection() { + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Wait 75us, then enable event detection, then wait another 75us. */ + util::WaitMicroSeconds(75); + reg::ReadWrite(address + APBDEV_PMC_CNTRL2, PMC_REG_BITS_ENUM(CNTRL2_WAKE_DET_EN, ENABLE)); + util::WaitMicroSeconds(75); + + /* Enable all wake events. */ + reg::Write(address + APBDEV_PMC_WAKE_STATUS, 0xFFFFFFFFu); + reg::Write(address + APBDEV_PMC_WAKE2_STATUS, 0xFFFFFFFFu); + util::WaitMicroSeconds(75); + } + + void ConfigureForSc7Entry() { + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Configure the bootrom to perform a warmboot. */ + reg::Write(address + APBDEV_PMC_SCRATCH0, 0x1); + + /* Enable the TSC multiplier. */ + reg::ReadWrite(address + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_TSC_MULT_EN, ENABLE)); + } + void LockSecureRegister(SecureRegister reg) { /* Get the address. */ const uintptr_t address = g_register_address; diff --git a/libraries/libexosphere/source/pmic/pmic_api.cpp b/libraries/libexosphere/source/pmic/pmic_api.cpp index e50723dc6..96847f898 100644 --- a/libraries/libexosphere/source/pmic/pmic_api.cpp +++ b/libraries/libexosphere/source/pmic/pmic_api.cpp @@ -25,13 +25,17 @@ namespace ams::pmic { constexpr inline int I2cAddressMarikoMax77812_A = 0x31; constexpr inline int I2cAddressMarikoMax77812_B = 0x33; + constexpr inline int I2cAddressMax77620Pmic = 0x3C; + /* https://github.com/Atmosphere-NX/Atmosphere/blob/master/emummc/source/power/max77620.h */ /* https://github.com/Atmosphere-NX/Atmosphere/blob/master/emummc/source/power/max7762x.h */ /* TODO: Find datasheet, link to it instead. */ /* NOTE: Tentatively, Max77620 "mostly" matches https://datasheets.maximintegrated.com/en/ds/MAX77863.pdf. */ /* This does not contain Max77621 documentation, though. */ - constexpr inline int Max77620RegisterGpio0 = 0x36; - constexpr inline int Max77620RegisterAmeGpio = 0x40; + constexpr inline int Max77620RegisterOnOffStat = 0x15; + constexpr inline int Max77620RegisterGpio0 = 0x36; + constexpr inline int Max77620RegisterAmeGpio = 0x40; + constexpr inline int Max77620RegisterOnOffCnfg1 = 0x41; constexpr inline int Max77621RegisterVOut = 0x00; constexpr inline int Max77621RegisterVOutDvc = 0x01; @@ -108,6 +112,10 @@ namespace ams::pmic { } } + u8 GetPmicOnOffStat() { + return i2c::QueryByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffStat); + } + } void EnableVddCpu(Regulator regulator) { @@ -132,4 +140,19 @@ namespace ams::pmic { } } + void EnableSleep() { + /* Get the current onoff cfg. */ + u8 cnfg = i2c::QueryByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffCnfg1); + + /* Set SlpEn. */ + cnfg |= (1 << 2); + + /* Write the new cfg. */ + i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffCnfg1, cnfg); + } + + bool IsAcOk() { + return (GetPmicOnOffStat() & (1 << 1)) != 0; + } + } From 34098f7215ff2cdb4074abe5fe4add2179d18b06 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 7 Jun 2020 19:51:49 -0700 Subject: [PATCH 083/118] exo2: add sc7fw load, skeleton rest of suspend --- exosphere2/program/Makefile | 11 ++- .../smc/secmon_smc_power_management.cpp | 80 ++++++++++++++++++- .../include/exosphere/tegra/tegra_clkrst.hpp | 7 +- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/Makefile b/exosphere2/program/Makefile index 078b88fd9..427061ca9 100644 --- a/exosphere2/program/Makefile +++ b/exosphere2/program/Makefile @@ -21,7 +21,8 @@ export TOPDIR := $(CURDIR) export DEPSDIR := $(CURDIR)/$(BUILD) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(TOPDIR)/sc7fw 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)))) @@ -41,6 +42,8 @@ SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATM 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 := sc7fw.bin + #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- @@ -71,16 +74,20 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_L #--------------------------------------------------------------------------------- all: $(BUILD) check_libexo -$(BUILD): check_libexo +$(BUILD): check_libexo check_sc7fw @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile check_libexo: @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64 +check_sc7fw: + @$(MAKE) -C $(TOPDIR)/sc7fw all + #--------------------------------------------------------------------------------- clean: @echo clean ... + @$(MAKE) -C $(TOPDIR)/sc7fw clean @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 #--------------------------------------------------------------------------------- diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 7596903cb..4398a6961 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -20,6 +20,8 @@ #include "secmon_smc_power_management.hpp" #include "secmon_smc_se_lock.hpp" +#include "sc7fw_bin.h" + namespace ams::secmon { /* Declare assembly functionality. */ @@ -35,9 +37,12 @@ namespace ams::secmon::smc { namespace { - constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); - constexpr inline uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress(); - constexpr inline uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline const uintptr_t APB_MISC = MemoryRegionVirtualDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress(); + constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress(); constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1)); @@ -150,8 +155,75 @@ namespace ams::secmon::smc { /* TODO */ } - void SaveSecureContextAndSuspend() { + void SaveSecureContextForErista() { /* TODO */ + } + + void SaveSecureContextForMariko() { + /* TODO */ + } + + void SaveSecureContext() { + const auto soc_type = GetSocType(); + if (soc_type == fuse::SocType_Erista) { + SaveSecureContextForErista(); + } else /* if (soc_type == fuse::SocType_Mariko) */ { + SaveSecureContextForMariko(); + } + } + + void LoadAndStartSc7BpmpFirmware() { + /* Set the PMC as insecure, so that the BPMP firmware can access it. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE)); + + /* Set the exception vectors for the bpmp. RESET should point to RESET, all others should point to generic exception/panic. */ + constexpr const u32 Sc7FirmwareResetVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0); + constexpr const u32 Sc7FirmwarePanicVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x4); + + reg::Write(EVP + EVP_COP_RESET_VECTOR, Sc7FirmwareResetVector); + reg::Write(EVP + EVP_COP_UNDEF_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_SWI_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_RSVD_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_IRQ_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_FIQ_VECTOR, Sc7FirmwarePanicVector); + + /* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */ + actmon::StopMonitoringBpmp(); + + /* Load the bpmp firmware. */ + void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer<void>(); + std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size); + hw::FlushDataCache(sc7fw_load_address, sc7fw_bin_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Ensure that the bpmp firmware was loaded. */ + AMS_ABORT_UNLESS(crypto::IsSameBytes(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size)); + + /* Clear BPMP reset. */ + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_COP_RST, ENABLE)); + + /* Start the bpmp. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_NONE)); + } + + void SaveSecureContextAndSuspend() { + /* Ensure there are no pending memory transactions before we continue */ + FlushEntireDataCache(); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Save all secure context (security engine state + tzram). */ + SaveSecureContext(); + + /* Load and start the sc7 firmware on the bpmp. */ + LoadAndStartSc7BpmpFirmware(); + + /* Log our suspension. */ + /* NOTE: Nintendo only does this on dev, but we will always do it. */ + if (true /* !pkg1::IsProduction() */) { + log::SendText("OYASUMI\n", 8); + } /* Finalize our powerdown and wait for an interrupt. */ FinalizePowerOff(); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 3659194aa..f6a1ae6b8 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -61,7 +61,10 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) -#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3e8) +#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8) + +/* RST_DEV_*_CLR */ +#define CLK_RST_CONTROLLER_RST_DEV_L_CLR (0x304) /* CLK_ENB_*_INDEX */ #define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C) @@ -96,3 +99,5 @@ DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); + +DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE); From 2fb363dcf01bd7a0069d5100f5637f05641d9db9 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 00:41:27 -0700 Subject: [PATCH 084/118] exo2: implement the rest of cpu suspend (security checks TODO) --- exosphere2/program/source/secmon_setup.cpp | 2 +- .../smc/secmon_smc_power_management.cpp | 129 +++++++++++- .../exosphere/pkg1/pkg1_se_key_slots.hpp | 3 +- .../libexosphere/include/exosphere/pmc.hpp | 4 +- .../include/exosphere/se/se_aes.hpp | 3 + .../include/exosphere/se/se_management.hpp | 3 +- .../include/exosphere/se/se_suspend.hpp | 3 +- .../exosphere/tegra/tegra_flow_ctlr.hpp | 2 +- .../include/exosphere/tegra/tegra_pmc.hpp | 2 + libraries/libexosphere/source/se/se_aes.cpp | 39 ++++ .../libexosphere/source/se/se_registers.hpp | 10 + .../libexosphere/source/se/se_suspend.cpp | 192 ++++++++++++++++++ 12 files changed, 384 insertions(+), 8 deletions(-) diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index a6a9ce914..f81642763 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -1007,7 +1007,7 @@ namespace ams::secmon { se::InitializeRandom(); se::SetRandomKey(pkg1::AesKeySlot_Temporary); se::GenerateSrk(); - se::SetRandomKey(pkg1::AesKeySlot_TzramSave); + se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); /* Initialize pmc secure scratch. */ pmc::InitializeRandomScratch(); diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 4398a6961..bc6c8f580 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -17,6 +17,7 @@ #include "../secmon_cache.hpp" #include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" +#include "../secmon_misc.hpp" #include "secmon_smc_power_management.hpp" #include "secmon_smc_se_lock.hpp" @@ -152,11 +153,137 @@ namespace ams::secmon::smc { } void ValidateSocStateForSuspend() { + /* Validate that all other cores are off. */ + AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); + + /* Validate that the bpmp is appropriately halted. */ + AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) != reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); + /* TODO */ } + void GenerateCryptographicallyRandomBytes(void * const dst, int size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + } + void SaveSecureContextForErista() { - /* TODO */ + /* Generate a random key source. */ + util::AlignedBuffer<hw::DataCacheLineSize, se::AesBlockSize> key_source; + GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize); + + const u32 * const key_source_32 = reinterpret_cast<const u32 *>(static_cast<u8 *>(key_source)); + + /* Ensure that the key source registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked); + + /* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked); + + /* Generate a random kek into keyslot 2. */ + se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); + + /* Verify that the se is in a validate state, context save, and validate again. */ + { + se::ValidateErrStatus(); + ON_SCOPE_EXIT { se::ValidateErrStatus(); }; + + { + /* Transition to non-secure mode for the duration of the context save operation. */ + se::SetSecure(false); + ON_SCOPE_EXIT { se::SetSecure(true); }; + + /* Get a pointer to the context storage. */ + se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer<se::Context>(); + static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context)); + + /* Save the context. */ + se::SaveContext(context); + + /* Ensure that the cpu sees consistent data. */ + hw::FlushDataCache(context, sizeof(*context)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress()); + } + } + + /* Clear keyslot 3, and then derive the save key. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + + /* Declare a temporary block to be used as both iv and mac. */ + u32 temp_block[se::AesBlockSize / sizeof(u32)] = {}; + + /* Ensure that the SE sees consistent data for tzram. */ + const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer<u8>() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize(); + void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer<void>(); + constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize(); + + hw::FlushDataCache(tzram_save_src, TzramSaveSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Encrypt tzram using our random key. */ + se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the data from work space to the secure storage destination. */ + void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer<void>(); + std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize); + hw::FlushDataCache(tzram_store_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Compute cmac of tzram into our temporary block. */ + se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize); + + /* Ensure that the cmac registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked); + + /* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked); } void SaveSecureContextForMariko() { diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp index e7c7074f8..ead3d4124 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -21,7 +21,8 @@ namespace ams::pkg1 { enum AesKeySlot { AesKeySlot_UserStart = 0, - AesKeySlot_TzramSave = 2, + AesKeySlot_TzramSaveKek = 2, + AesKeySlot_TzramSaveKey = 3, AesKeySlot_UserLast = 5, AesKeySlot_UserEnd = AesKeySlot_UserLast + 1, diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp index 207307ebc..d8034a146 100644 --- a/libraries/libexosphere/include/exosphere/pmc.hpp +++ b/libraries/libexosphere/include/exosphere/pmc.hpp @@ -42,8 +42,8 @@ namespace ams::pmc { void LockSecureRegister(SecureRegister reg); enum class LockState { - Locked = 0, - NotLocked = 1, + Locked = 0, + NotLocked = 1, PartiallyLocked = 2, }; diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index 942f9155d..bd8c46ced 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -38,6 +38,9 @@ namespace ams::se { void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void EncryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void EncryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); diff --git a/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp index 05369d1fa..41469f594 100644 --- a/libraries/libexosphere/include/exosphere/se/se_management.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -30,6 +30,7 @@ namespace ams::se { void HandleInterrupt(); + void ValidateErrStatus(); void ValidateAesOperationResult(); -} \ No newline at end of file +} diff --git a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp index 9f16924b7..33cc7a15a 100644 --- a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp @@ -51,5 +51,6 @@ namespace ams::se { static_assert(util::is_pod<StickyBits>::value); bool ValidateStickyBits(const StickyBits &bits); + void SaveContext(Context *dst); -} \ No newline at end of file +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index 8bc203ff3..eebdcc400 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -61,7 +61,7 @@ DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_IRQN, 11, DISABLE, ENABLE); DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, 29, NONE, RUN_AND_INT, WAITEVENT, WAITEVENT_AND_INT, STOP_UNTIL_IRQ, STOP_UNTIL_EVENT_AND_IRQ, RESERVED6, RESERVED7); -DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, ENABLED, DISABLED); +DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, DISABLED, ENABLED); DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MODE_RUN_AND_INT, FLOW_MODE_STOP, FLOW_MODE_STOP_AND_INT, FLOW_MODE_STOP_UNTIL_IRQ, FLOW_MODE_STOP_UNTIL_IRQ_AND_INT, FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ, RESERVED7); DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index c6ede5f01..98d3743d8 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -185,6 +185,8 @@ DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_AUD, 27, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DFD, 28, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON); +DEFINE_PMC_REG(PWRGATE_STATUS_CE123, 9, 3); + DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1); DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index c46e02171..5ded61a13 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -277,6 +277,37 @@ namespace ams::se { GetCmacResult(SE, dst, dst_size); } + void EncryptAesCbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size, AesMode mode) { + /* If nothing to encrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine extents. */ + const size_t num_blocks = src_size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + AMS_ABORT_UNLESS(src_size == aligned_size); + + /* Configure for aes-cbc encryption. */ + SetConfig(SE, true, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, true, AesConfigCbcEncrypt); + UpdateAesMode(SE, mode); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Set the block count. */ + SetBlockCount(SE, num_blocks); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size); + } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { /* If nothing to decrypt, succeed. */ if (size == 0) { return; } @@ -448,6 +479,14 @@ namespace ams::se { return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes256); } + void EncryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes128); + } + + void EncryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256); + } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { /* Validate the iv. */ AMS_ABORT_UNLESS(iv_size == AesBlockSize); diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp index 2a5e70352..f66360a88 100644 --- a/libraries/libexosphere/source/se/se_registers.hpp +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -158,6 +158,16 @@ namespace ams::se { SE_CONFIG_ENC_MODE_SHA512 = 7, }; + /* SE_CTX_SAVE_CONFIG */ + DEFINE_SE_REG_TWO_BIT_ENUM(CTX_SAVE_CONFIG_AES_WORD_QUAD, 0, KEYS_0_3, KEYS_4_7, ORIGINAL_IVS, UPDATED_IVS); + DEFINE_SE_REG(CTX_SAVE_CONFIG_PKA1_WORD_QUAD_L, 0, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_AES_KEY_INDEX, 8, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_RSA_WORD_QUAD, 12, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_PKA1_WORD_QUAD_H, 12, 4); + DEFINE_SE_REG_TWO_BIT_ENUM(CTX_SAVE_CONFIG_RSA_KEY_INDEX, 16, SLOT0_EXPONENT, SLOT0_MODULUS, SLOT1_EXPONENT, SLOT1_MODULUS); + DEFINE_SE_REG_BIT_ENUM(CTX_SAVE_CONFIG_STICKY_WORD_QUAD, 24, WORDS_0_3, WORDS_4_7); + DEFINE_SE_REG_THREE_BIT_ENUM(CTX_SAVE_CONFIG_SRC, 29, STICKY_BITS, RSA_KEYTABLE, AES_KEYTABLE, PKA1_STICKY_BITS, MEM, RESERVED5, SRK, PKA1_KEYTABLE); + /* SE_SHA_CONFIG */ DEFINE_SE_REG(SHA_CONFIG_HW_INIT_HASH, 0, 1); diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp index ca5134a35..98d49c958 100644 --- a/libraries/libexosphere/source/se/se_suspend.cpp +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -20,10 +20,50 @@ namespace ams::se { namespace { + constinit const u8 FixedPattern[AesBlockSize] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F + }; + bool TestRegister(volatile u32 &r, u16 v) { return (static_cast<u16>(reg::Read(r))) == v; } + void ExecuteContextSaveOperation(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Save the output to a temporary buffer. */ + util::AlignedBuffer<hw::DataCacheLineSize, AesBlockSize> temp; + AMS_ABORT_UNLESS(dst_size <= AesBlockSize); + + /* Ensure that the cpu and SE see consistent data. */ + if (src_size > 0) { + hw::FlushDataCache(src, src_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + if (dst_size > 0) { + hw::FlushDataCache(temp, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + } + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_CTX_SAVE, dst, dst_size, src, src_size); + + /* Copy output from the operation, if any. */ + if (dst_size > 0) { + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(temp, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + std::memcpy(dst, temp, dst_size); + } + } + + void SaveContextBlock(volatile SecurityEngineRegisters *SE, void *dst) { + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst, AesBlockSize, nullptr, 0); + } + } bool ValidateStickyBits(const StickyBits &bits) { @@ -56,4 +96,156 @@ namespace ams::se { return true; } + void SaveContext(Context *dst) { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Generate a random srk. */ + GenerateSrk(); + + /* Save a randomly-generated block. */ + { + util::AlignedBuffer<hw::DataCacheLineSize, AesBlockSize> random_block; + + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + GenerateRandomBytes(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure to encrypt the random block to memory. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, AES_ENC), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, MEMORY)); + + /* Configure to context save using memory as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, MEM)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst->random, AesBlockSize, random_block, AesBlockSize); + } + + /* Save the sticky bits. */ + for (size_t i = 0; i < util::size(dst->sticky_bits); ++i) { + /* Configure to encrypt the sticky bits block. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, STICKY_BITS), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_STICKY_WORD_QUAD, i)); + + /* Save the block. */ + SaveContextBlock(SE, dst->sticky_bits[i]); + } + + /* Save the aes keytable. */ + { + for (size_t key = 0; key < util::size(dst->aes_key); ++key) { + for (auto part = 0; part < AesKeySlotPartCount; ++part) { + /* Configure to encrypt the part of the key. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_WORD_QUAD, part)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_key[key][part]); + } + } + + for (size_t key = 0; key < util::size(dst->aes_oiv); ++key) { + /* Configure to encrypt the original iv. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_AES_WORD_QUAD, ORIGINAL_IVS)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_oiv[key]); + } + + for (size_t key = 0; key < util::size(dst->aes_uiv); ++key) { + /* Configure to encrypt the updated iv. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_AES_WORD_QUAD, UPDATED_IVS)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_uiv[key]); + } + } + + /* Save the rsa keytable. */ + for (size_t key = 0; key < util::size(dst->rsa_key); ++key) { + for (auto part = 0; part < RsaKeySlotPartCount; ++part) { + /* Note that the parts are done in reverse order. */ + const auto part_index = RsaKeySlotPartCount - 1 - part; + + /* Determine a total key index. */ + const auto key_index = key * util::size(dst->rsa_key) + part_index; + + for (size_t block = 0; block < RsaSize / AesBlockSize; ++block) { + /* Configure to encrypt the part of the key. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, RSA_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_RSA_KEY_INDEX, key_index), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_RSA_WORD_QUAD, block)); + + /* Save the block. */ + SaveContextBlock(SE, dst->rsa_key[key][part][block]); + } + } + } + + /* Save the fixed pattern. */ + { + /* Configure to context save using memory as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, MEM)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst->fixed_pattern, AesBlockSize, FixedPattern, AesBlockSize); + } + + /* Save the srk. */ + { + /* Configure to context save using srk as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, SRK)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, nullptr, 0, nullptr, 0); + } + + /* Perform a no-op context save operation. */ + { + /* Configure to perform no-op. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP)); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, nullptr, 0, nullptr, 0); + } + } + + void ValidateErrStatus() { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Ensure there is no error status. */ + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + + /* Ensure no error occurred. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); + } + } From 95d38a1a942ff0fc710b8e26272cacc7f6bdd018 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 03:53:40 -0700 Subject: [PATCH 085/118] exo2: suspend fixes (sleep/wake now works on hardware) --- exosphere/lp0fw/src/pmc.h | 6 +++- exosphere/lp0fw/src/se.c | 30 ++++++++++++++----- exosphere/lp0fw/src/se.h | 7 +++-- exosphere/lp0fw/src/secmon.c | 16 +++++++++- exosphere2/program/source/secmon_setup.cpp | 5 +++- .../smc/secmon_smc_power_management.cpp | 13 ++++++-- .../include/exosphere/tegra/tegra_clkrst.hpp | 5 ++++ .../include/exosphere/tegra/tegra_pmc.hpp | 2 ++ libraries/libexosphere/source/log/log_api.cpp | 9 ++++-- .../libexosphere/source/se/se_suspend.cpp | 2 +- 10 files changed, 77 insertions(+), 18 deletions(-) diff --git a/exosphere/lp0fw/src/pmc.h b/exosphere/lp0fw/src/pmc.h index cba043f19..a872c6073 100644 --- a/exosphere/lp0fw/src/pmc.h +++ b/exosphere/lp0fw/src/pmc.h @@ -13,7 +13,7 @@ * 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_PMC_H #define EXOSPHERE_WARMBOOT_BIN_PMC_H @@ -39,6 +39,10 @@ #define APBDEV_PMC_SEC_DISABLE2_0 MAKE_PMC_REG(0x2C4) #define APBDEV_PMC_WEAK_BIAS_0 MAKE_PMC_REG(0x2C8) #define APBDEV_PMC_SECURE_SCRATCH21_0 MAKE_PMC_REG(0x334) +#define APBDEV_PMC_SECURE_SCRATCH24_0 MAKE_PMC_REG(0x340) +#define APBDEV_PMC_SECURE_SCRATCH25_0 MAKE_PMC_REG(0x344) +#define APBDEV_PMC_SECURE_SCRATCH26_0 MAKE_PMC_REG(0x348) +#define APBDEV_PMC_SECURE_SCRATCH27_0 MAKE_PMC_REG(0x34C) #define APBDEV_PMC_SECURE_SCRATCH32_0 MAKE_PMC_REG(0x360) #define APBDEV_PMC_SECURE_SCRATCH34_0 MAKE_PMC_REG(0x368) #define APBDEV_PMC_SECURE_SCRATCH35_0 MAKE_PMC_REG(0x36C) diff --git a/exosphere/lp0fw/src/se.c b/exosphere/lp0fw/src/se.c index 3a0607f24..2deccbdbc 100644 --- a/exosphere/lp0fw/src/se.c +++ b/exosphere/lp0fw/src/se.c @@ -13,7 +13,7 @@ * 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 <string.h> #include "utils.h" @@ -56,7 +56,7 @@ void se_verify_flags_cleared(void) { void clear_aes_keyslot(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { reboot(); } @@ -70,7 +70,7 @@ void clear_aes_keyslot(unsigned int keyslot) { void clear_rsa_keyslot(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_RSA_MAX) { reboot(); } @@ -90,7 +90,7 @@ void clear_rsa_keyslot(unsigned int keyslot) { void clear_aes_keyslot_iv(unsigned int keyslot) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { reboot(); } @@ -101,6 +101,20 @@ void clear_aes_keyslot_iv(unsigned int keyslot) { } } +void decrypt_data_into_keyslot_256(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { + volatile tegra_se_t *se = se_get_regs(); + + if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSLOT_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { + reboot(); + } + + se->SE_CONFIG = (0x202 << 16) | (ALG_AES_DEC | DST_KEYTAB); + se->SE_CRYPTO_CONFIG = keyslot_src << 24; + se->SE_CRYPTO_LAST_BLOCK = 0; + se->SE_CRYPTO_KEYTABLE_DST = keyslot_dst << 8; + + trigger_se_blocking_op(OP_START, NULL, 0, wrapped_key, wrapped_key_size); +} void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size) { volatile tegra_se_t *se = se_get_regs(); @@ -148,7 +162,7 @@ void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { reboot(); } @@ -161,7 +175,7 @@ void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { reboot(); } @@ -185,7 +199,7 @@ void shift_left_xor_rb(uint8_t *key) { void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX) { reboot(); } @@ -239,7 +253,7 @@ void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, void se_aes_256_cbc_decrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { volatile tegra_se_t *se = se_get_regs(); - + if (keyslot >= KEYSLOT_AES_MAX || src_size < 0x10) { reboot(); } diff --git a/exosphere/lp0fw/src/se.h b/exosphere/lp0fw/src/se.h index 8612b169c..5e0083e36 100644 --- a/exosphere/lp0fw/src/se.h +++ b/exosphere/lp0fw/src/se.h @@ -13,14 +13,15 @@ * 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_SE_H #define EXOSPHERE_WARMBOOT_BIN_SE_H #define SE_BASE 0x70012000 #define MAKE_SE_REG(n) MAKE_REG32(SE_BASE + n) -#define KEYSLOT_SWITCH_LP0TZRAMKEY 0x2 +#define KEYSLOT_SWITCH_LP0TZRAMKEK 0x2 +#define KEYSLOT_SWITCH_LP0TZRAMKEY 0x3 #define KEYSLOT_SWITCH_SRKGENKEY 0x8 #define KEYSLOT_SWITCH_PACKAGE2KEY 0x8 #define KEYSLOT_SWITCH_TEMPKEY 0x9 @@ -170,6 +171,8 @@ void clear_aes_keyslot(unsigned int keyslot); void clear_rsa_keyslot(unsigned int keyslot); void clear_aes_keyslot_iv(unsigned int keyslot); +void decrypt_data_into_keyslot_256(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size); + void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); void se_aes_256_cbc_decrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); diff --git a/exosphere/lp0fw/src/secmon.c b/exosphere/lp0fw/src/secmon.c index f39c89fa9..01a6ff539 100644 --- a/exosphere/lp0fw/src/secmon.c +++ b/exosphere/lp0fw/src/secmon.c @@ -57,6 +57,20 @@ void secmon_restore_to_tzram(const uint32_t target_firmware) { } void secmon_decrypt_saved_image(void *dst, const void *src, size_t size) { + /* Derive the key used for context save. */ + { + const uint32_t key_source[4] = { APBDEV_PMC_SECURE_SCRATCH24_0, APBDEV_PMC_SECURE_SCRATCH25_0, APBDEV_PMC_SECURE_SCRATCH26_0, APBDEV_PMC_SECURE_SCRATCH27_0 }; + + clear_aes_keyslot(KEYSLOT_SWITCH_LP0TZRAMKEY); + decrypt_data_into_keyslot_256(KEYSLOT_SWITCH_LP0TZRAMKEY, KEYSLOT_SWITCH_LP0TZRAMKEK, key_source, sizeof(key_source)); + + clear_aes_keyslot(KEYSLOT_SWITCH_LP0TZRAMKEK); + APBDEV_PMC_SECURE_SCRATCH24_0 = 0; + APBDEV_PMC_SECURE_SCRATCH25_0 = 0; + APBDEV_PMC_SECURE_SCRATCH26_0 = 0; + APBDEV_PMC_SECURE_SCRATCH27_0 = 0; + } + /* First, AES-256-CBC decrypt the image into TZRAM. */ se_aes_256_cbc_decrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, dst, size, src, size); @@ -85,7 +99,7 @@ void secmon_decrypt_saved_image(void *dst, const void *src, size_t size) { bool secmon_should_clear_aes_keyslot(unsigned int keyslot) { /* We'll just compare keyslot against a hardcoded list of keys. */ static const uint8_t saved_keyslots[6] = { - KEYSLOT_SWITCH_LP0TZRAMKEY, + KEYSLOT_SWITCH_LP0TZRAMKEK, KEYSLOT_SWITCH_SESSIONKEY, KEYSLOT_SWITCH_RNGKEY, KEYSLOT_SWITCH_MASTERKEY, diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index f81642763..254b8e51f 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -695,7 +695,7 @@ namespace ams::secmon { /* Setup sctlr_el2. */ { - util::BitPack64 sctlr = { hw::SctlrEl2::Res1 }; // 0x30C5083 + util::BitPack64 sctlr = { hw::SctlrEl2::Res1 }; sctlr.Set<hw::SctlrEl2::M>(0); /* Globally disable the MMU. */ sctlr.Set<hw::SctlrEl2::A>(0); /* Disable alignment fault checking. */ @@ -1087,6 +1087,9 @@ namespace ams::secmon { /* Perform initial setup. */ Setup1ForWarmboot(); + /* Generate a random srk. */ + se::GenerateSrk(); + /* Setup the Soc security. */ SetupSocSecurity(); diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index bc6c8f580..93f053887 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -157,7 +157,7 @@ namespace ams::secmon::smc { AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); /* Validate that the bpmp is appropriately halted. */ - AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) != reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); /* TODO */ @@ -236,7 +236,7 @@ namespace ams::secmon::smc { /* Clear keyslot 3, and then derive the save key. */ se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); - se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, se::AesBlockSize); /* Declare a temporary block to be used as both iv and mac. */ u32 temp_block[se::AesBlockSize / sizeof(u32)] = {}; @@ -319,6 +319,9 @@ namespace ams::secmon::smc { /* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */ actmon::StopMonitoringBpmp(); + /* Set BPMP reset. */ + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE)); + /* Load the bpmp firmware. */ void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer<void>(); std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size); @@ -352,6 +355,12 @@ namespace ams::secmon::smc { log::SendText("OYASUMI\n", 8); } + /* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */ + if (GetSocType() == fuse::SocType_Erista) { + reg::Write(PMC + APBDEV_PMC_SCRATCH31, 0x2202E012); + reg::Write(PMC + APBDEV_PMC_SCRATCH32, 0x6001DC28); + } + /* Finalize our powerdown and wait for an interrupt. */ FinalizePowerOff(); } diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index f6a1ae6b8..fc2253c27 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -63,6 +63,9 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) #define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8) +/* RST_DEV_*_SET */ +#define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300) + /* RST_DEV_*_CLR */ #define CLK_RST_CONTROLLER_RST_DEV_L_CLR (0x304) @@ -100,4 +103,6 @@ DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE); + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index 98d3743d8..f64f500f5 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -42,6 +42,8 @@ #define APBDEV_PMC_WAKE_DELAY (0x0E0) #define APBDEV_PMC_PWR_DET_VAL (0x0E4) #define APBDEV_PMC_CRYPTO_OP (0x0F4) +#define APBDEV_PMC_SCRATCH31 (0x118) +#define APBDEV_PMC_SCRATCH32 (0x11C) #define APBDEV_PMC_WAKE2_MASK (0x160) #define APBDEV_PMC_WAKE2_LVL (0x164) #define APBDEV_PMC_WAKE2_STATUS (0x168) diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp index 2ea3c5c70..ad1d89ea2 100644 --- a/libraries/libexosphere/source/log/log_api.cpp +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -21,6 +21,7 @@ namespace ams::log { constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug; constinit bool g_initialized_uart = false; + constinit bool g_logging_enabled = false; constexpr inline u32 UartPortFlags = [] { if constexpr (UartLogPort == uart::Port_ReservedDebug) { @@ -75,14 +76,18 @@ namespace ams::log { g_initialized_uart = false; } + void SetDebugLogEnabled(bool en) { + g_logging_enabled = en; + } + void SendText(const void *text, size_t size) { - if (g_initialized_uart) { + if (g_initialized_uart && g_logging_enabled) { uart::SendText(UartLogPort, text, size); } } void Flush() { - if (g_initialized_uart) { + if (g_initialized_uart && g_logging_enabled) { uart::WaitFlush(UartLogPort); } } diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp index 98d49c958..883ae3cbc 100644 --- a/libraries/libexosphere/source/se/se_suspend.cpp +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -44,7 +44,7 @@ namespace ams::se { } /* Execute the operation. */ - ExecuteOperation(SE, SE_OPERATION_OP_CTX_SAVE, dst, dst_size, src, src_size); + ExecuteOperation(SE, SE_OPERATION_OP_CTX_SAVE, temp, dst_size, src, src_size); /* Copy output from the operation, if any. */ if (dst_size > 0) { From bb6671a94a45dea9cfb251118b1404fc3a42d918 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 05:17:52 -0700 Subject: [PATCH 086/118] exo2: implement SmcReencryptDeviceUniqueData --- .../program/source/smc/secmon_smc_aes.cpp | 106 ++++++++++++++---- .../smc/secmon_smc_device_unique_data.cpp | 69 ++++++++++-- .../smc/secmon_smc_device_unique_data.hpp | 10 ++ .../source/smc/secmon_smc_memory_access.cpp | 2 +- libraries/config/templates/exosphere.mk | 4 +- libraries/libexosphere/arm64.mk | 2 +- 6 files changed, 163 insertions(+), 30 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index aab91f842..a3790c0fd 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -517,22 +517,7 @@ namespace ams::secmon::smc { return SmcResult::Success; } - SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { - /* Decode arguments. */ - u8 access_key[se::AesBlockSize]; - u8 key_source[se::AesBlockSize]; - - std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); - const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; - const uintptr_t data_address = args.r[4]; - const size_t data_size = args.r[5]; - std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); - - const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); - const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); - - /* Validate arguments. */ - SMC_R_UNLESS(reserved == 0, InvalidArgument); + SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) { switch (mode) { case DeviceUniqueData_DecryptDeviceUniqueData: { @@ -551,8 +536,29 @@ namespace ams::secmon::smc { return SmcResult::InvalidArgument; } + return SmcResult::Success; + } + + SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[se::AesBlockSize]; + u8 key_source[se::AesBlockSize]; + + std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); + + const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + /* Decrypt the device unique data. */ - u8 work_buffer[DeviceUniqueDataSizeMax]; + alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax]; ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); }; { /* Map and copy in the encrypted data. */ @@ -593,6 +599,69 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult ReencryptDeviceUniqueDataImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key_dec[se::AesBlockSize]; + u8 access_key_enc[se::AesBlockSize]; + u8 key_source_dec[se::AesBlockSize]; + u8 key_source_enc[se::AesBlockSize]; + + const uintptr_t access_key_dec_address = args.r[1]; + const uintptr_t access_key_enc_address = args.r[2]; + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + const uintptr_t key_source_dec_address = args.r[6]; + const uintptr_t key_source_enc_address = args.r[7]; + + const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + + /* Decrypt the device unique data. */ + alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax]; + ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); }; + { + /* Map and copy in the encrypted data. */ + UserPageMapper mapper(data_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(access_key_dec, access_key_dec_address, sizeof(access_key_dec)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(access_key_enc, access_key_enc_address, sizeof(access_key_enc)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(key_source_dec, key_source_dec_address, sizeof(key_source_dec)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(key_source_enc, key_source_enc_address, sizeof(key_source_enc)), InvalidArgument); + + /* Decrypt the data. */ + u8 device_id_high; + { + /* Determine the seal key to use. */ + const u8 * const seal_key_source = SealKeySources[SealKey_ReencryptDeviceUniqueData]; + + if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size)) { + return SmcResult::InvalidArgument; + } + } + + /* Reencrypt the data. */ + { + /* Determine the seal key to use. */ + const auto seal_key_type = DeviceUniqueDataToSealKey[mode]; + const u8 * const seal_key_source = SealKeySources[seal_key_type]; + + /* Encrypt the data. */ + EncryptDeviceUniqueData(work_buffer, data_size, seal_key_source, se::AesBlockSize, access_key_enc, sizeof(access_key_enc), key_source_enc, sizeof(key_source_enc), work_buffer, data_size - DeviceUniqueDataTotalMetaSize, device_id_high); + } + + /* Copy the reencrypted data back to user. */ + SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument); + } + + return SmcResult::Success; + } + SmcResult GetSecureDataImpl(SmcArguments &args) { /* Decode arguments. */ const auto which = static_cast<SecureData>(args.r[1]); @@ -647,8 +716,7 @@ namespace ams::secmon::smc { } SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, ReencryptDeviceUniqueDataImpl); } /* Legacy APIs. */ diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp index e14dfbe18..6505daa52 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -21,14 +21,19 @@ namespace ams::secmon::smc { namespace { - constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize; - constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; - constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); - constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; + void GenerateIv(void *dst, size_t dst_size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); - constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; - constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; - constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) { /* Derive the seal key. */ @@ -79,6 +84,10 @@ namespace ams::secmon::smc { return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); } + constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) { + return (static_cast<u64>(device_id_high) << (BITSIZEOF(u64) - BITSIZEOF(u8))) | device_id_low; + } + } bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { @@ -143,4 +152,50 @@ namespace ams::secmon::smc { return true; } + void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high) { + /* Determine metadata locations. */ + u8 * const dst_iv = static_cast<u8 *>(dst); + u8 * const dst_data = dst_iv + DeviceUniqueDataIvSize; + u8 * const dst_pad = dst_data + src_size; + u8 * const dst_did = dst_pad + DeviceUniqueDataPaddingSize; + u8 * const dst_mac = dst_did + DeviceUniqueDataDeviceIdSize; + + /* Verify that our sizes are okay. */ + const size_t enc_size = src_size + DeviceUniqueDataInnerMetaSize; + const size_t res_size = src_size + DeviceUniqueDataTotalMetaSize; + AMS_ABORT_UNLESS(res_size <= dst_size); + + /* Layout the image as expected. */ + { + /* Generate a random iv. */ + util::AlignedBuffer<hw::DataCacheLineSize, DeviceUniqueDataIvSize> iv; + GenerateIv(iv, DeviceUniqueDataIvSize); + + /* Move the data to the output image. */ + std::memmove(dst_data, src, src_size); + + /* Copy the iv. */ + std::memcpy(dst_iv, iv, DeviceUniqueDataIvSize); + + /* Clear the padding. */ + std::memset(dst_pad, 0, DeviceUniqueDataPaddingSize); + + /* Store the device id. */ + util::StoreBigEndian(reinterpret_cast<u64 *>(dst_did), EncodeDeviceId(device_id_high, fuse::GetDeviceId())); + } + + /* Encrypt and mac. */ + { + + /* Prepare the key used to encrypt the data. */ + PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); + + /* Compute the gmac. */ + ComputeGmac(dst_mac, DeviceUniqueDataMacSize, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + + /* Encrypt the data. */ + ComputeAes128Ctr(dst_data, enc_size, pkg1::AesKeySlot_Smc, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + } + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp index 92fc5b509..a8e790151 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -19,6 +19,16 @@ namespace ams::secmon::smc { + constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); + constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; + + constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; + constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; + constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size); + void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high); } diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp index d68dcf879..a4071a832 100644 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -26,7 +26,7 @@ namespace ams::secmon::smc { } SmcResult SmcWriteAddress(SmcArguments &args) { - /* TODO */ + /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ return SmcResult::NotImplemented; } diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk index 163626659..7025e615e 100644 --- a/libraries/config/templates/exosphere.mk +++ b/libraries/config/templates/exosphere.mk @@ -8,7 +8,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk #--------------------------------------------------------------------------------- ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) @@ -20,7 +20,7 @@ CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) endif -export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-exceptions -fno-rtti -fno-use-cxa-atexit -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,__cxa_throw \ diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk index c9a17e60d..35492a4c9 100644 --- a/libraries/libexosphere/arm64.mk +++ b/libraries/libexosphere/arm64.mk @@ -14,7 +14,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) From 6c145d76c7c1b9e511b3f34ee1f81394442192a6 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 09:02:50 -0700 Subject: [PATCH 087/118] exo2: implement SmcIramCopy/reboot to payload/rcm --- exosphere2/Makefile | 2 +- exosphere2/program/Makefile | 11 +- exosphere2/program/rebootstub/Makefile | 122 ++++++++++++ exosphere2/program/rebootstub/rebootstub.ld | 183 ++++++++++++++++++ .../program/rebootstub/rebootstub.specs | 7 + .../source/rebootstub_exception_vectors.s | 25 +++ .../rebootstub/source/rebootstub_main.s | 50 +++++ .../source/rebootstub_power_off.cpp | 61 ++++++ exosphere2/program/sc7fw/sc7fw.ld | 2 +- exosphere2/program/source/secmon_map.cpp | 12 ++ .../source/{smc => }/secmon_page_mapper.cpp | 8 +- .../source/{smc => }/secmon_page_mapper.hpp | 11 +- .../source/secmon_user_power_management.cpp | 95 +++++++++ .../source/secmon_user_power_management.hpp | 31 +++ .../program/source/smc/secmon_smc_aes.cpp | 2 +- .../program/source/smc/secmon_smc_info.cpp | 34 +++- .../source/smc/secmon_smc_memory_access.cpp | 46 ++++- .../program/source/smc/secmon_smc_result.cpp | 2 +- .../program/source/smc/secmon_smc_rsa.cpp | 2 +- libraries/libexosphere/arm.mk | 4 +- libraries/libexosphere/arm64.mk | 2 +- libraries/libexosphere/include/exosphere.hpp | 1 + .../include/exosphere/hw/hw_arm64_cache.hpp | 6 +- .../exosphere/mmu/mmu_api.arch.arm64.hpp | 34 ++-- .../libexosphere/include/exosphere/pmic.hpp | 1 + .../libexosphere/include/exosphere/rtc.hpp | 23 +++ .../exosphere/secmon/secmon_memory_layout.hpp | 2 + .../include/exosphere/tegra/tegra_pmc.hpp | 4 + .../libexosphere/source/pmic/pmic_api.cpp | 7 +- .../libexosphere/source/rtc/max77620-rtc.h | 60 ++++++ libraries/libexosphere/source/rtc/rtc_api.cpp | 65 +++++++ 31 files changed, 868 insertions(+), 47 deletions(-) create mode 100644 exosphere2/program/rebootstub/Makefile create mode 100644 exosphere2/program/rebootstub/rebootstub.ld create mode 100644 exosphere2/program/rebootstub/rebootstub.specs create mode 100644 exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s create mode 100644 exosphere2/program/rebootstub/source/rebootstub_main.s create mode 100644 exosphere2/program/rebootstub/source/rebootstub_power_off.cpp rename exosphere2/program/source/{smc => }/secmon_page_mapper.cpp (90%) rename exosphere2/program/source/{smc => }/secmon_page_mapper.hpp (81%) create mode 100644 exosphere2/program/source/secmon_user_power_management.cpp create mode 100644 exosphere2/program/source/secmon_user_power_management.hpp create mode 100644 libraries/libexosphere/include/exosphere/rtc.hpp create mode 100644 libraries/libexosphere/source/rtc/max77620-rtc.h create mode 100644 libraries/libexosphere/source/rtc/rtc_api.cpp diff --git a/exosphere2/Makefile b/exosphere2/Makefile index 9b72ff1ec..20619aed9 100644 --- a/exosphere2/Makefile +++ b/exosphere2/Makefile @@ -11,6 +11,7 @@ clean: $(CLEAN_TARGETS) 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..." program.lz4: check_libexo @@ -32,7 +33,6 @@ program-clean: @rm -f program.lz4 boot_code-clean: - $(MAKE) -C boot_code clean @rm -f boot_code.lz4 .PHONY: all clean $(CLEAN_TARGETS) diff --git a/exosphere2/program/Makefile b/exosphere2/program/Makefile index 427061ca9..23b18af76 100644 --- a/exosphere2/program/Makefile +++ b/exosphere2/program/Makefile @@ -22,7 +22,8 @@ export DEPSDIR := $(CURDIR)/$(BUILD) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(TOPDIR)/sc7fw + $(TOPDIR)/sc7fw \ + $(TOPDIR)/rebootstub 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)))) @@ -42,7 +43,7 @@ SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATM 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 := sc7fw.bin +BINFILES := sc7fw.bin rebootstub.bin #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -74,20 +75,22 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_L #--------------------------------------------------------------------------------- all: $(BUILD) check_libexo -$(BUILD): check_libexo check_sc7fw +$(BUILD): check_libexo check_firmwares @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile check_libexo: @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64 -check_sc7fw: +check_firmwares: @$(MAKE) -C $(TOPDIR)/sc7fw all + @$(MAKE) -C $(TOPDIR)/rebootstub all #--------------------------------------------------------------------------------- clean: @echo clean ... @$(MAKE) -C $(TOPDIR)/sc7fw clean + @$(MAKE) -C $(TOPDIR)/rebootstub clean @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 #--------------------------------------------------------------------------------- diff --git a/exosphere2/program/rebootstub/Makefile b/exosphere2/program/rebootstub/Makefile new file mode 100644 index 000000000..31439e834 --- /dev/null +++ b/exosphere2/program/rebootstub/Makefile @@ -0,0 +1,122 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# 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)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +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) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/rebootstub/rebootstub.ld b/exosphere2/program/rebootstub/rebootstub.ld new file mode 100644 index 000000000..c37c7555f --- /dev/null +++ b/exosphere2/program/rebootstub/rebootstub.ld @@ -0,0 +1,183 @@ +OUTPUT_ARCH(arm) +ENTRY(reset) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + rebootstub : ORIGIN = 0x4003F000, LENGTH = 4K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(rebootstub)); + . = __start__; + __code_start = . ; + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >rebootstub + + .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); + } >rebootstub + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >rebootstub + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >rebootstub + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >rebootstub + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >rebootstub + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >rebootstub + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >rebootstub + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >rebootstub + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >rebootstub + + .hash : { *(.hash) } >rebootstub + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >rebootstub + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >rebootstub + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >rebootstub + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >rebootstub + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >rebootstub + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >rebootstub + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >rebootstub + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >rebootstub + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >rebootstub + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >rebootstub + .got.plt : { *(.got.plt) *(.igot.plt) } >rebootstub + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >rebootstub + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >rebootstub + __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) } +} \ No newline at end of file diff --git a/exosphere2/program/rebootstub/rebootstub.specs b/exosphere2/program/rebootstub/rebootstub.specs new file mode 100644 index 000000000..4e41b1615 --- /dev/null +++ b/exosphere2/program/rebootstub/rebootstub.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s b/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s new file mode 100644 index 000000000..eaf62f151 --- /dev/null +++ b/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s @@ -0,0 +1,25 @@ +/* + * 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/>. + */ + +.section .vectors, "ax", %progbits +.align 3 +.global reset +reset: + b _ZN3ams10rebootstub4MainEv + +.global _ZN3ams10rebootstub10RebootTypeE +_ZN3ams10rebootstub10RebootTypeE: +.word 0x00000001 \ No newline at end of file diff --git a/exosphere2/program/rebootstub/source/rebootstub_main.s b/exosphere2/program/rebootstub/source/rebootstub_main.s new file mode 100644 index 000000000..03e3ec618 --- /dev/null +++ b/exosphere2/program/rebootstub/source/rebootstub_main.s @@ -0,0 +1,50 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams10rebootstub4MainEv, "ax", %progbits +.align 3 +.global _ZN3ams10rebootstub4MainEv +_ZN3ams10rebootstub4MainEv: + /* Get the reboot type. */ + ldr r0, =_ZN3ams10rebootstub10RebootTypeE + ldr r0, [r0] + + /* If the reboot type is power off, perform a power off. */ + cmp r0, #0 + beq _ZN3ams10rebootstub8PowerOffEv + + /* Otherwise, clear all registers jump to the reboot payload in iram. */ + ldr r0, =0x52425430 /* RBT0 */ + mov r1, #0 + mov r2, #0 + mov r3, #0 + mov r4, #0 + mov r5, #0 + mov r5, #0 + mov r6, #0 + mov r7, #0 + mov r8, #0 + mov r9, #0 + mov r10, #0 + mov r11, #0 + mov r12, #0 + mov r9, #0 + mov lr, #0 + ldr sp, =0x40010000 + ldr pc, =0x40010000 + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp new file mode 100644 index 000000000..97d0e210f --- /dev/null +++ b/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp @@ -0,0 +1,61 @@ +/* + * 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> + +namespace ams::rebootstub { + + NORETURN void Halt() { + while (true) { + reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED)); + } + + __builtin_unreachable(); + } + + NORETURN void PowerOff() { + /* Ensure that i2c5 is usable. */ + clkrst::EnableI2c5Clock(); + + /* Initialize i2c5. */ + i2c::Initialize(i2c::Port_5); + + /* Stop rtc alarms. */ + rtc::StopAlarm(); + + /* Perform a pmic power off. */ + pmic::PowerOff(); + + /* Halt the bpmp. */ + Halt(); + + /* This can never be reached. */ + __builtin_unreachable(); + } + +} + +namespace ams::diag { + + void AbortImpl() { + /* Halt the bpmp. */ + rebootstub::Halt(); + + /* This can never be reached. */ + __builtin_unreachable(); + } + +} diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere2/program/sc7fw/sc7fw.ld index e8a097dbb..794edc3d3 100644 --- a/exosphere2/program/sc7fw/sc7fw.ld +++ b/exosphere2/program/sc7fw/sc7fw.ld @@ -1,5 +1,5 @@ OUTPUT_ARCH(arm) -ENTRY(_start) +ENTRY(reset) MEMORY { diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index 92100ad2f..96ccf84ed 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -171,6 +171,10 @@ namespace ams::secmon { return; } + /* Ensure that the page is no longer in cache. */ + hw::FlushDataCache(MemoryRegionVirtualSmcUserPage.GetPointer<void>(), MemoryRegionVirtualSmcUserPage.GetSize()); + hw::DataSynchronizationBarrierInnerShareable(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); UnmapSmcUserPageImpl(l2_l3); @@ -218,6 +222,10 @@ namespace ams::secmon { return; } + /* Ensure that the page is no longer in cache. */ + hw::FlushDataCache(MemoryRegionVirtualAtmosphereIramPage.GetPointer<void>(), MemoryRegionVirtualAtmosphereIramPage.GetSize()); + hw::DataSynchronizationBarrierInnerShareable(); + /* Unmap the page. */ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); @@ -269,6 +277,10 @@ namespace ams::secmon { return; } + /* Ensure that the page is no longer in cache. */ + hw::FlushDataCache(MemoryRegionVirtualAtmosphereUserPage.GetPointer<void>(), MemoryRegionVirtualAtmosphereUserPage.GetSize()); + hw::DataSynchronizationBarrierInnerShareable(); + /* Unmap the page. */ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>(); diff --git a/exosphere2/program/source/smc/secmon_page_mapper.cpp b/exosphere2/program/source/secmon_page_mapper.cpp similarity index 90% rename from exosphere2/program/source/smc/secmon_page_mapper.cpp rename to exosphere2/program/source/secmon_page_mapper.cpp index 8d2008907..93452d4fe 100644 --- a/exosphere2/program/source/smc/secmon_page_mapper.cpp +++ b/exosphere2/program/source/secmon_page_mapper.cpp @@ -14,10 +14,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> -#include "../secmon_map.hpp" +#include "secmon_map.hpp" #include "secmon_page_mapper.hpp" -namespace ams::secmon::smc { +namespace ams::secmon { namespace impl { @@ -35,7 +35,7 @@ namespace ams::secmon::smc { return reinterpret_cast<void *>(phys + (this->virtual_address - this->physical_address)); } - bool PageMapperImpl::CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { + bool PageMapperImpl::CopyToMapping(uintptr_t dst_phys, const void *src, size_t size) const { void * const dst = this->GetPointerTo(dst_phys, size); if (dst == nullptr) { return false; @@ -45,7 +45,7 @@ namespace ams::secmon::smc { return true; } - bool PageMapperImpl::CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { + bool PageMapperImpl::CopyFromMapping(void *dst, uintptr_t src_phys, size_t size) const { const void * const src = this->GetPointerTo(src_phys, size); if (src == nullptr) { return false; diff --git a/exosphere2/program/source/smc/secmon_page_mapper.hpp b/exosphere2/program/source/secmon_page_mapper.hpp similarity index 81% rename from exosphere2/program/source/smc/secmon_page_mapper.hpp rename to exosphere2/program/source/secmon_page_mapper.hpp index a57f70812..1da95ebc3 100644 --- a/exosphere2/program/source/smc/secmon_page_mapper.hpp +++ b/exosphere2/program/source/secmon_page_mapper.hpp @@ -15,9 +15,8 @@ */ #pragma once #include <exosphere.hpp> -#include "secmon_smc_common.hpp" -namespace ams::secmon::smc { +namespace ams::secmon { namespace impl { @@ -29,8 +28,12 @@ namespace ams::secmon::smc { constexpr PageMapperImpl(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ } void *GetPointerTo(uintptr_t phys, size_t size) const; - bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const; - bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const; + + bool CopyToMapping(uintptr_t dst_phys, const void *src, size_t size) const; + bool CopyFromMapping(void *dst, uintptr_t src_phys, size_t size) const; + + ALWAYS_INLINE bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { return CopyToMapping(dst_phys, src, size); } + ALWAYS_INLINE bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { return CopyFromMapping(dst, src_phys, size); } template<auto F> bool MapImpl() { diff --git a/exosphere2/program/source/secmon_user_power_management.cpp b/exosphere2/program/source/secmon_user_power_management.cpp new file mode 100644 index 000000000..4fb063b96 --- /dev/null +++ b/exosphere2/program/source/secmon_user_power_management.cpp @@ -0,0 +1,95 @@ +/* + * 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_page_mapper.hpp" +#include "secmon_user_power_management.hpp" + +#include "rebootstub_bin.h" + +namespace ams::secmon { + + namespace { + + constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + + constexpr inline const u32 RebootStubPhysicalAddress = MemoryRegionPhysicalIramRebootStub.GetAddress(); + + enum RebootStubAction { + RebootStubAction_ShutDown = 0, + RebootStubAction_JumpToPayload = 1, + }; + + NORETURN void PerformPmcReboot() { + /* Write MAIN_RST. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, 0x10); + + while (true) { + /* ... */ + } + } + + void LoadRebootStub(u32 action) { + /* Configure the bootrom to boot to warmboot payload. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x1); + + /* Patch the bootrom to perform an SVC immediately after the second spare write. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH45, 0x2E38DFFF); + reg::Write(PMC + APBDEV_PMC_SCRATCH46, 0x6001DC28); + + /* Patch the bootrom to jump to the reboot stub we'll prepare in iram on SVC. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH33, RebootStubPhysicalAddress); + reg::Write(PMC + APBDEV_PMC_SCRATCH40, 0x6000F208); + + { + /* Map the iram page. */ + AtmosphereIramPageMapper mapper(RebootStubPhysicalAddress); + AMS_ABORT_UNLESS(mapper.Map()); + + /* Copy the reboot stub. */ + AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress, rebootstub_bin, rebootstub_bin_size)); + + /* Set the reboot type. */ + AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress + 4, std::addressof(action), sizeof(action))); + } + } + + } + + void PerformUserRebootToRcm() { + /* Configure the bootrom to boot to rcm. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x2); + + /* Reboot. */ + PerformPmcReboot(); + } + + void PerformUserRebootToPayload() { + /* Load our reboot stub to iram. */ + LoadRebootStub(RebootStubAction_JumpToPayload); + + /* Reboot. */ + PerformPmcReboot(); + } + + void PerformUserShutDown() { + /* Load our reboot stub to iram. */ + LoadRebootStub(RebootStubAction_ShutDown); + + /* Reboot. */ + PerformPmcReboot(); + } + +} diff --git a/exosphere2/program/source/secmon_user_power_management.hpp b/exosphere2/program/source/secmon_user_power_management.hpp new file mode 100644 index 000000000..9a50e8227 --- /dev/null +++ b/exosphere2/program/source/secmon_user_power_management.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::secmon { + + enum UserRebootType { + UserRebootType_None = 0, + UserRebootType_ToRcm = 1, + UserRebootType_ToPayload = 2, + }; + + void PerformUserRebootToRcm(); + void PerformUserRebootToPayload(); + void PerformUserShutDown(); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index a3790c0fd..cfb992e33 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -17,10 +17,10 @@ #include "../secmon_error.hpp" #include "../secmon_key_storage.hpp" #include "../secmon_misc.hpp" +#include "../secmon_page_mapper.hpp" #include "secmon_smc_aes.hpp" #include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_se_lock.hpp" -#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp index c36ef37b2..7cf50269d 100644 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -16,7 +16,8 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "../secmon_misc.hpp" -#include "secmon_page_mapper.hpp" +#include "../secmon_page_mapper.hpp" +#include "../secmon_user_power_management.hpp" #include "secmon_smc_info.hpp" #include "secmon_smc_power_management.hpp" @@ -269,17 +270,40 @@ namespace ams::secmon::smc { } SmcResult SetConfig(SmcArguments &args) { + const auto soc_type = GetSocType(); + switch (static_cast<ConfigItem>(args.r[1])) { case ConfigItem::IsChargerHiZModeEnabled: /* Configure the HiZ mode. */ SetChargerHiZModeEnabled(static_cast<bool>(args.r[3])); break; case ConfigItem::ExosphereNeedsReboot: - /* TODO */ - return SmcResult::NotImplemented; + if (soc_type == fuse::SocType_Erista) { + switch (static_cast<UserRebootType>(args.r[3])) { + case UserRebootType_None: + break; + case UserRebootType_ToRcm: + PerformUserRebootToRcm(); + break; + case UserRebootType_ToPayload: + PerformUserRebootToPayload(); + break; + default: + return SmcResult::InvalidArgument; + } + } else /* if (soc_type == fuse::SocType_Mariko) */ { + return SmcResult::NotImplemented; + } + break; case ConfigItem::ExosphereNeedsShutdown: - /* TODO */ - return SmcResult::NotImplemented; + if (soc_type == fuse::SocType_Erista) { + if (args.r[3] != 0) { + PerformUserShutDown(); + } + } else /* if (soc_type == fuse::SocType_Mariko) */ { + return SmcResult::NotImplemented; + } + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp index a4071a832..dba249ccf 100644 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -15,14 +15,56 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_page_mapper.hpp" #include "secmon_smc_memory_access.hpp" namespace ams::secmon::smc { + namespace { + + enum IramCopyType { + IramCopyType_FromIramToDram = 0, + IramCopyType_FromDramToIram = 1, + IramCopyType_Count, + }; + + struct IramCopyOption { + using CopyType = util::BitPack32::Field<0, 1, IramCopyType>; + }; + + } + /* This is an atmosphere extension smc. */ SmcResult SmcIramCopy(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const uintptr_t dram_address = args.r[1]; + const uintptr_t iram_address = args.r[2]; + const size_t size = args.r[3]; + const util::BitPack32 option = { static_cast<u32>(args.r[4]) }; + + const auto copy_type = option.Get<IramCopyOption::CopyType>(); + + /* Validate arguments. */ + SMC_R_UNLESS(copy_type < IramCopyType_Count, InvalidArgument); + + { + /* Map the pages. */ + AtmosphereUserPageMapper dram_mapper(dram_address); + AtmosphereIramPageMapper iram_mapper(iram_address); + SMC_R_UNLESS(dram_mapper.Map(), InvalidArgument); + SMC_R_UNLESS(iram_mapper.Map(), InvalidArgument); + + /* Get the ranges we're copying. */ + const void * const src = (copy_type == IramCopyType_FromIramToDram) ? iram_mapper.GetPointerTo(iram_address, size) : dram_mapper.GetPointerTo(dram_address, size); + void * const dst = (copy_type == IramCopyType_FromIramToDram) ? dram_mapper.GetPointerTo(dram_address, size) : iram_mapper.GetPointerTo(iram_address, size); + SMC_R_UNLESS(src != nullptr, InvalidArgument); + SMC_R_UNLESS(dst != nullptr, InvalidArgument); + + /* Copy the data. */ + std::memcpy(dst, src, size); + } + + return SmcResult::Success; } SmcResult SmcWriteAddress(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp index 83fa1b404..246f50cd3 100644 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -15,8 +15,8 @@ */ #include <exosphere.hpp> #include "../secmon_error.hpp" +#include "../secmon_page_mapper.hpp" #include "secmon_smc_result.hpp" -#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp index 20b44711d..d84ce9b14 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -16,10 +16,10 @@ #include <exosphere.hpp> #include "../secmon_error.hpp" #include "../secmon_key_storage.hpp" +#include "../secmon_page_mapper.hpp" #include "secmon_smc_aes.hpp" #include "secmon_smc_rsa.hpp" #include "secmon_smc_se_lock.hpp" -#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk index 7a653f53b..5819d270c 100644 --- a/libraries/libexosphere/arm.mk +++ b/libraries/libexosphere/arm.mk @@ -14,7 +14,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -flto -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) @@ -125,7 +125,7 @@ $(OFILES) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) -libc.o: CFLAGS += -fno-builtin +libc.o: CFLAGS += -fno-builtin -fno-lto #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk index 35492a4c9..1ad716a3e 100644 --- a/libraries/libexosphere/arm64.mk +++ b/libraries/libexosphere/arm64.mk @@ -126,7 +126,7 @@ $(OFILES) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) -libc.o: CFLAGS += -fno-builtin +libc.o: CFLAGS += -fno-builtin -fno-lto #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index e93e8b8fa..d7403cbf5 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -35,6 +35,7 @@ #include <exosphere/uart.hpp> #include <exosphere/pinmux.hpp> #include <exosphere/pmic.hpp> +#include <exosphere/rtc.hpp> #include <exosphere/log.hpp> #include <exosphere/clkrst.hpp> #include <exosphere/actmon.hpp> diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp index ac534fe14..547014f4f 100644 --- a/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp @@ -43,11 +43,13 @@ namespace ams::hw::arch::arm64 { } ALWAYS_INLINE void InvalidateTlb(uintptr_t address) { - __asm__ __volatile__("tlbi vae3is, %[address]" :: [address]"r"(address) : "memory"); + const uintptr_t page_index = address / 4_KB; + __asm__ __volatile__("tlbi vae3is, %[page_index]" :: [page_index]"r"(page_index) : "memory"); } ALWAYS_INLINE void InvalidateTlbLastLevel(uintptr_t address) { - __asm__ __volatile__("tlbi vale3is, %[address]" :: [address]"r"(address) : "memory"); + const uintptr_t page_index = address / 4_KB; + __asm__ __volatile__("tlbi vale3is, %[page_index]" :: [page_index]"r"(page_index) : "memory"); } void FlushDataCache(const void *ptr, size_t size); diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp index 07fea9c43..d4d0c970f 100644 --- a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp @@ -140,7 +140,7 @@ namespace ams::mmu::arch::arm64 { constexpr inline u64 MemoryRegionAttributeWidth = 8; - constexpr PageTableMappingAttribute AddMappingAttributeIndex(PageTableMappingAttribute attr, int index) { + constexpr ALWAYS_INLINE PageTableMappingAttribute AddMappingAttributeIndex(PageTableMappingAttribute attr, int index) { return static_cast<PageTableMappingAttribute>(attr | (static_cast<typename std::underlying_type<PageTableMappingAttribute>::type>(index) << 2)); } @@ -169,35 +169,35 @@ namespace ams::mmu::arch::arm64 { constexpr inline u64 EntryBlock = 0x1ul; constexpr inline u64 EntryPage = 0x3ul; - constexpr u64 MakeTableEntry(u64 address, PageTableTableAttribute attr) { + constexpr ALWAYS_INLINE u64 MakeTableEntry(u64 address, PageTableTableAttribute attr) { return address | static_cast<u64>(attr) | 0x3ul; } - constexpr u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) { return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x1ul; } - constexpr u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) { return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x1ul; } - constexpr u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) { return address | static_cast<u64>(attr) | static_cast<u64>(PageTableMappingAttribute_AccessFlagAccessed) | 0x3ul; } - constexpr uintptr_t GetL2Offset(uintptr_t address) { + constexpr ALWAYS_INLINE uintptr_t GetL2Offset(uintptr_t address) { return address & ((1ul << L2EntryShift) - 1); } - constexpr u64 GetL1EntryIndex(uintptr_t address) { + constexpr ALWAYS_INLINE u64 GetL1EntryIndex(uintptr_t address) { return ((address >> L1EntryShift) & TableEntryIndexMask); } - constexpr u64 GetL2EntryIndex(uintptr_t address) { + constexpr ALWAYS_INLINE u64 GetL2EntryIndex(uintptr_t address) { return ((address >> L2EntryShift) & TableEntryIndexMask); } - constexpr u64 GetL3EntryIndex(uintptr_t address) { + constexpr ALWAYS_INLINE u64 GetL3EntryIndex(uintptr_t address) { return ((address >> L3EntryShift) & TableEntryIndexMask); } @@ -218,15 +218,15 @@ namespace ams::mmu::arch::arm64 { SetTableEntryImpl(table, index, value); } - constexpr void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + constexpr ALWAYS_INLINE void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { SetTableEntry(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); } - constexpr void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + constexpr ALWAYS_INLINE void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { SetTableEntry(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); } - constexpr void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { const u64 start = GetL1EntryIndex(virt_addr); const u64 count = (size >> L1EntryShift); @@ -235,7 +235,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void SetL2BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE void SetL2BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { const u64 start = GetL2EntryIndex(virt_addr); const u64 count = (size >> L2EntryShift); @@ -244,7 +244,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void SetL3BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + constexpr ALWAYS_INLINE void SetL3BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { const u64 start = GetL3EntryIndex(virt_addr); const u64 count = (size >> L3EntryShift); @@ -253,7 +253,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void InvalidateL1Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { + constexpr ALWAYS_INLINE void InvalidateL1Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL1EntryIndex(virt_addr); const u64 count = (size >> L1EntryShift); const u64 end = start + count; @@ -263,7 +263,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void InvalidateL2Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { + constexpr ALWAYS_INLINE void InvalidateL2Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL2EntryIndex(virt_addr); const u64 count = (size >> L2EntryShift); const u64 end = start + count; @@ -273,7 +273,7 @@ namespace ams::mmu::arch::arm64 { } } - constexpr void InvalidateL3Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { + constexpr ALWAYS_INLINE void InvalidateL3Entries(volatile u64 *table, uintptr_t virt_addr, size_t size) { const u64 start = GetL3EntryIndex(virt_addr); const u64 count = (size >> L3EntryShift); const u64 end = start + count; diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp index 1d01eb811..29122bce2 100644 --- a/libraries/libexosphere/include/exosphere/pmic.hpp +++ b/libraries/libexosphere/include/exosphere/pmic.hpp @@ -30,6 +30,7 @@ namespace ams::pmic { void EnableVddCpu(Regulator regulator); void DisableVddCpu(Regulator regulator); void EnableSleep(); + void PowerOff(); bool IsAcOk(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/rtc.hpp b/libraries/libexosphere/include/exosphere/rtc.hpp new file mode 100644 index 000000000..bacf1b9f1 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/rtc.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +namespace ams::rtc { + + void StopAlarm(); + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index 38e6452bf..c6ec12b37 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -287,4 +287,6 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionPhysicalIramWarmbootBin = MemoryRegion(UINT64_C(0x4003E000), 0x17F0); constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootConfig = MemoryRegion(UINT64_C(0x4003F800), 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramRebootStub = MemoryRegion(UINT64_C(0x4003F000), 0x1000); + } diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index f64f500f5..da1c6d053 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -44,6 +44,8 @@ #define APBDEV_PMC_CRYPTO_OP (0x0F4) #define APBDEV_PMC_SCRATCH31 (0x118) #define APBDEV_PMC_SCRATCH32 (0x11C) +#define APBDEV_PMC_SCRATCH33 (0x120) +#define APBDEV_PMC_SCRATCH40 (0x13C) #define APBDEV_PMC_WAKE2_MASK (0x160) #define APBDEV_PMC_WAKE2_LVL (0x164) #define APBDEV_PMC_WAKE2_STATUS (0x168) @@ -54,6 +56,8 @@ #define APBDEV_PMC_IO_DPD2_REQ (0x1C0) #define APBDEV_PMC_IO_DPD2_STATUS (0x1C4) #define APBDEV_PMC_SEL_DPD_TIM (0x1C8) +#define APBDEV_PMC_SCRATCH45 (0x234) +#define APBDEV_PMC_SCRATCH46 (0x238) #define APBDEV_PMC_TSC_MULT (0x2B4) #define APBDEV_PMC_WEAK_BIAS (0x2C8) #define APBDEV_PMC_GPU_RG_CNTRL (0x2D4) diff --git a/libraries/libexosphere/source/pmic/pmic_api.cpp b/libraries/libexosphere/source/pmic/pmic_api.cpp index 96847f898..bf1515d7c 100644 --- a/libraries/libexosphere/source/pmic/pmic_api.cpp +++ b/libraries/libexosphere/source/pmic/pmic_api.cpp @@ -145,12 +145,17 @@ namespace ams::pmic { u8 cnfg = i2c::QueryByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffCnfg1); /* Set SlpEn. */ - cnfg |= (1 << 2); + cnfg |= MAX77620_ONOFFCNFG1_SLPEN; /* Write the new cfg. */ i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffCnfg1, cnfg); } + void PowerOff() { + /* Write power-off to onoff cfg. */ + i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, Max77620RegisterOnOffCnfg1, MAX77620_ONOFFCNFG1_PWR_OFF); + } + bool IsAcOk() { return (GetPmicOnOffStat() & (1 << 1)) != 0; } diff --git a/libraries/libexosphere/source/rtc/max77620-rtc.h b/libraries/libexosphere/source/rtc/max77620-rtc.h new file mode 100644 index 000000000..2d38487cc --- /dev/null +++ b/libraries/libexosphere/source/rtc/max77620-rtc.h @@ -0,0 +1,60 @@ +/* + * PMIC Real Time Clock driver for Nintendo Switch's MAX77620-RTC + * + * 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, + * 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 _MFD_MAX77620_RTC_H_ +#define _MFD_MAX77620_RTC_H_ + +#define MAX77620_RTC_I2C_ADDR 0x68 + +#define MAX77620_RTC_NR_TIME_REGS 7 + +#define MAX77620_RTC_CONTROLM_REG 0x02 +#define MAX77620_RTC_CONTROL_REG 0x03 +#define MAX77620_RTC_BIN_FORMAT (1 << 0) +#define MAX77620_RTC_24H (1 << 1) + +#define MAX77620_RTC_UPDATE0_REG 0x04 +#define MAX77620_RTC_WRITE_UPDATE (1 << 0) +#define MAX77620_RTC_READ_UPDATE (1 << 4) + +#define MAX77620_RTC_SEC_REG 0x07 +#define MAX77620_RTC_MIN_REG 0x08 +#define MAX77620_RTC_HOUR_REG 0x09 +#define MAX77620_RTC_HOUR_PM_MASK (1 << 6) +#define MAX77620_RTC_WEEKDAY_REG 0x0A +#define MAX77620_RTC_MONTH_REG 0x0B +#define MAX77620_RTC_YEAR_REG 0x0C +#define MAX77620_RTC_DATE_REG 0x0D + +#define MAX77620_ALARM1_SEC_REG 0x0E +#define MAX77620_ALARM1_MIN_REG 0x0F +#define MAX77620_ALARM1_HOUR_REG 0x10 +#define MAX77620_ALARM1_WEEKDAY_REG 0x11 +#define MAX77620_ALARM1_MONTH_REG 0x12 +#define MAX77620_ALARM1_YEAR_REG 0x13 +#define MAX77620_ALARM1_DATE_REG 0x14 +#define MAX77620_ALARM2_SEC_REG 0x15 +#define MAX77620_ALARM2_MIN_REG 0x16 +#define MAX77620_ALARM2_HOUR_REG 0x17 +#define MAX77620_ALARM2_WEEKDAY_REG 0x18 +#define MAX77620_ALARM2_MONTH_REG 0x19 +#define MAX77620_ALARM2_YEAR_REG 0x1A +#define MAX77620_ALARM2_DATE_REG 0x1B +#define MAX77620_RTC_ALARM_EN_MASK (1 << 7) + +#endif /* _MFD_MAX77620_RTC_H_ */ diff --git a/libraries/libexosphere/source/rtc/rtc_api.cpp b/libraries/libexosphere/source/rtc/rtc_api.cpp new file mode 100644 index 000000000..dcf8480ee --- /dev/null +++ b/libraries/libexosphere/source/rtc/rtc_api.cpp @@ -0,0 +1,65 @@ +/* + * 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 "max77620-rtc.h" + +namespace ams::rtc { + + namespace { + + constexpr inline int I2cAddressMax77620Rtc = 0x68; + + /* TODO: Find datasheet, link to it instead. */ + /* NOTE: Tentatively, Max77620 "mostly" matches https://datasheets.maximintegrated.com/en/ds/MAX77863.pdf. */ + constexpr inline int Max77620RtcRegisterUpdate0 = 0x04; + + constexpr inline int Max77620RtcRegisterAlarmStart = 0x0E; + + constexpr inline int Max77620RtcRegisterAlarm1Sec = 0x0E; + constexpr inline int Max77620RtcRegisterAlarm1Min = 0x0F; + constexpr inline int Max77620RtcRegisterAlarm1Hour = 0x10; + constexpr inline int Max77620RtcRegisterAlarm1Weekday = 0x11; + constexpr inline int Max77620RtcRegisterAlarm1Month = 0x12; + constexpr inline int Max77620RtcRegisterAlarm1Year = 0x13; + constexpr inline int Max77620RtcRegisterAlarm1Date = 0x14; + constexpr inline int Max77620RtcRegisterAlarm2Sec = 0x15; + constexpr inline int Max77620RtcRegisterAlarm2Min = 0x16; + constexpr inline int Max77620RtcRegisterAlarm2Hour = 0x17; + constexpr inline int Max77620RtcRegisterAlarm2Weekday = 0x18; + constexpr inline int Max77620RtcRegisterAlarm2Month = 0x19; + constexpr inline int Max77620RtcRegisterAlarm2Year = 0x1A; + constexpr inline int Max77620RtcRegisterAlarm2Date = 0x1B; + + constexpr inline int Max77620RtcRegisterAlarmLast = 0x1B; + + } + + void StopAlarm() { + /* Begin update. */ + i2c::SendByte(i2c::Port_5, I2cAddressMax77620Rtc, Max77620RtcRegisterUpdate0, MAX77620_RTC_READ_UPDATE); + + /* Clear ALARM_EN for all alarm registers. */ + for (auto reg = Max77620RtcRegisterAlarmStart; reg <= Max77620RtcRegisterAlarmLast; ++reg) { + u8 val = i2c::QueryByte(i2c::Port_5, I2cAddressMax77620Rtc, reg); + val &= ~MAX77620_RTC_ALARM_EN_MASK; + i2c::SendByte(i2c::Port_5, I2cAddressMax77620Rtc, reg, val); + } + + /* End update. */ + i2c::SendByte(i2c::Port_5, I2cAddressMax77620Rtc, Max77620RtcRegisterUpdate0, MAX77620_RTC_WRITE_UPDATE); + } + +} From f82954e98b1156f1dda20f45a2a9e5411fc59483 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 16:26:55 -0700 Subject: [PATCH 088/118] git subrepo clone --force --branch=exo2 https://github.com/m4xw/emummc subrepo: subdir: "emummc" merged: "3791be9f" upstream: origin: "https://github.com/m4xw/emummc" branch: "exo2" commit: "3791be9f" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- emummc/.gitrepo | 6 +- emummc/Makefile | 5 +- emummc/source/emmc/mmc.h | 4 +- emummc/source/emmc/nx_sd.c | 91 +++ emummc/source/emmc/nx_sd.h | 36 ++ emummc/source/emmc/sd.h | 11 +- emummc/source/emmc/sdmmc.c | 516 ++++++++--------- emummc/source/emmc/sdmmc.h | 8 +- emummc/source/emmc/sdmmc_driver.c | 793 +++++++++++++++------------ emummc/source/emmc/sdmmc_driver.h | 195 ++++++- emummc/source/emmc/sdmmc_t210.h | 110 ++-- emummc/source/emuMMC/emummc.c | 92 ++-- emummc/source/emuMMC/emummc.h | 7 +- emummc/source/emuMMC/emummc_ctx.h | 2 +- emummc/source/libs/fatfs/ff.c | 272 ++++++++- emummc/source/libs/fatfs/ff.h | 14 +- emummc/source/libs/fatfs/ffconf.h | 11 +- emummc/source/libs/fatfs/ffunicode.c | 2 - emummc/source/power/max77620.h | 57 +- emummc/source/power/max7762x.c | 6 - emummc/source/power/max7762x.h | 9 +- emummc/source/soc/clock.c | 199 +++++-- emummc/source/soc/clock.h | 35 +- emummc/source/soc/i2c.c | 25 +- emummc/source/soc/pmc.h | 4 + emummc/source/soc/t210.h | 2 + emummc/source/utils/fatal.h | 4 + emummc/source/utils/util.c | 4 +- emummc/source/utils/util.h | 4 +- 29 files changed, 1653 insertions(+), 871 deletions(-) create mode 100644 emummc/source/emmc/nx_sd.c create mode 100644 emummc/source/emmc/nx_sd.h diff --git a/emummc/.gitrepo b/emummc/.gitrepo index df68f68fb..df0193066 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = https://github.com/m4xw/emuMMC - branch = develop - commit = 292a8ad42c8e9f4c9a474b46a5a3190398581131 - parent = 491ba8fdcfd39a503bedd21b282991fc19aec7d4 + branch = exo2 + commit = 3791be9fd207811cce221a4311da63fda1d32d4a + parent = ff7bed5db7d441eecc9444a9068ae59e7f9b7744 method = rebase cmdver = 0.4.1 diff --git a/emummc/Makefile b/emummc/Makefile index 81e02c1cb..f902447aa 100644 --- a/emummc/Makefile +++ b/emummc/Makefile @@ -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__ diff --git a/emummc/source/emmc/mmc.h b/emummc/source/emmc/mmc.h index dddb956e9..0d87e3556 100644 --- a/emummc/source/emmc/mmc.h +++ b/emummc/source/emmc/mmc.h @@ -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 */ diff --git a/emummc/source/emmc/nx_sd.c b/emummc/source/emmc/nx_sd.c new file mode 100644 index 000000000..1f5fd50d2 --- /dev/null +++ b/emummc/source/emmc/nx_sd.c @@ -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; +} \ No newline at end of file diff --git a/emummc/source/emmc/nx_sd.h b/emummc/source/emmc/nx_sd.h new file mode 100644 index 000000000..38c74edbb --- /dev/null +++ b/emummc/source/emmc/nx_sd.h @@ -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 \ No newline at end of file diff --git a/emummc/source/emmc/sd.h b/emummc/source/emmc/sd.h index c3bf82ba0..3ecfc073a 100644 --- a/emummc/source/emmc/sd.h +++ b/emummc/source/emmc/sd.h @@ -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 */ diff --git a/emummc/source/emmc/sdmmc.c b/emummc/source/emmc/sdmmc.c index 69452dea3..db17aac0d 100644 --- a/emummc/source/emmc/sdmmc.c +++ b/emummc/source/emmc/sdmmc.c @@ -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; } diff --git a/emummc/source/emmc/sdmmc.h b/emummc/source/emmc/sdmmc.h index fc9e54831..96c83be12 100644 --- a/emummc/source/emmc/sdmmc.h +++ b/emummc/source/emmc/sdmmc.h @@ -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); diff --git a/emummc/source/emmc/sdmmc_driver.c b/emummc/source/emmc/sdmmc_driver.c index 7e20c3786..fc63f6e5c 100644 --- a/emummc/source/emmc/sdmmc_driver.c +++ b/emummc/source/emmc/sdmmc_driver.c @@ -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, @@ -16,18 +16,18 @@ */ #include <string.h> -#include "../utils/types.h" -#include "../nx/cache.h" -#include "sdmmc.h" -#include "../utils/util.h" -#include "../soc/clock.h" #include "mmc.h" +#include "sdmmc.h" +#include "../nx/cache.h" #include "../power/max7762x.h" -#include "../soc/t210.h" -#include "../soc/pmc.h" -#include "../soc/pinmux.h" +#include "../soc/clock.h" #include "../soc/gpio.h" +#include "../soc/pinmux.h" +#include "../soc/pmc.h" +#include "../soc/t210.h" #include "../utils/fatal.h" +#include "../utils/types.h" +#include "../utils/util.h" #define DPRINTF(...) @@ -39,44 +39,37 @@ static const u64 _sdmmc_bases[4] = { 0x700B0600, }; -int sdmmc_get_voltage(sdmmc_t *sdmmc) +int sdmmc_get_io_power(sdmmc_t *sdmmc) { u32 p = sdmmc->regs->pwrcon; - if (!(p & TEGRA_MMC_PWRCTL_SD_BUS_POWER)) + if (!(p & SDHCI_POWER_ON)) return SDMMC_POWER_OFF; - if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8) + if (p & SDHCI_POWER_180) return SDMMC_POWER_1_8; - if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3) + if (p & SDHCI_POWER_330) return SDMMC_POWER_3_3; return -1; } -static int _sdmmc_set_voltage(sdmmc_t *sdmmc, u32 power) +static int _sdmmc_set_io_power(sdmmc_t *sdmmc, u32 power) { - u8 pwr = 0; - switch (power) { case SDMMC_POWER_OFF: - sdmmc->regs->pwrcon &= ~TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->regs->pwrcon &= ~SDHCI_POWER_ON; break; case SDMMC_POWER_1_8: - sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; - pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + sdmmc->regs->pwrcon = SDHCI_POWER_180; break; case SDMMC_POWER_3_3: - sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; - pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + sdmmc->regs->pwrcon = SDHCI_POWER_330; break; default: return 0; } if (power != SDMMC_POWER_OFF) - { - pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; - sdmmc->regs->pwrcon = pwr; - } + sdmmc->regs->pwrcon |= SDHCI_POWER_ON; return 1; } @@ -84,48 +77,52 @@ static int _sdmmc_set_voltage(sdmmc_t *sdmmc, u32 power) u32 sdmmc_get_bus_width(sdmmc_t *sdmmc) { u32 h = sdmmc->regs->hostctl; - if (h & TEGRA_MMC_HOSTCTL_8BIT) + if (h & SDHCI_CTRL_8BITBUS) return SDMMC_BUS_WIDTH_8; - if (h & TEGRA_MMC_HOSTCTL_4BIT) + if (h & SDHCI_CTRL_4BITBUS) return SDMMC_BUS_WIDTH_4; return SDMMC_BUS_WIDTH_1; } void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width) { + u32 host_control = sdmmc->regs->hostctl & ~(SDHCI_CTRL_4BITBUS | SDHCI_CTRL_8BITBUS); + if (bus_width == SDMMC_BUS_WIDTH_1) - sdmmc->regs->hostctl &= ~(TEGRA_MMC_HOSTCTL_4BIT | TEGRA_MMC_HOSTCTL_8BIT); + sdmmc->regs->hostctl = host_control; else if (bus_width == SDMMC_BUS_WIDTH_4) - { - sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_4BIT; - sdmmc->regs->hostctl &= ~TEGRA_MMC_HOSTCTL_8BIT; - } + sdmmc->regs->hostctl = host_control | SDHCI_CTRL_4BITBUS; else if (bus_width == SDMMC_BUS_WIDTH_8) - sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_8BIT; + sdmmc->regs->hostctl = host_control | SDHCI_CTRL_8BITBUS; } -void sdmmc_get_venclkctl(sdmmc_t *sdmmc) +void sdmmc_set_tap_value(sdmmc_t *sdmmc) { sdmmc->venclkctl_tap = sdmmc->regs->venclkctl >> 16; sdmmc->venclkctl_set = 1; } -static int _sdmmc_config_ven_ceata_clk(sdmmc_t *sdmmc, u32 id) +static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type) { + const u32 dqs_trim_val = 0x28; + const u32 tap_values[] = { 4, 0, 3, 0 }; + u32 tap_val = 0; - if (id == 4) - sdmmc->regs->venceatactl = (sdmmc->regs->venceatactl & 0xFFFFC0FF) | 0x2800; - sdmmc->regs->ventunctl0 &= 0xFFFDFFFF; - if (id == 4) + if (type == SDHCI_TIMING_MMC_HS400) + sdmmc->regs->vencapover = (sdmmc->regs->vencapover & 0xFFFFC0FF) | (dqs_trim_val << 8); + + sdmmc->regs->ventunctl0 &= ~TEGRA_MMC_VNDR_TUN_CTRL0_TAP_VAL_UPDATED_BY_HW; + + if (type == SDHCI_TIMING_MMC_HS400) { if (!sdmmc->venclkctl_set) return 0; + tap_val = sdmmc->venclkctl_tap; } else { - static const u32 tap_values[] = { 4, 0, 3, 0 }; tap_val = tap_values[sdmmc->id]; } sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16); @@ -143,163 +140,250 @@ static void _sdmmc_pad_config_fallback(sdmmc_t *sdmmc, u32 power) _sdmmc_get_clkcon(sdmmc); switch (sdmmc->id) { - case SDMMC_1: + case SDMMC_1: // 33 Ohm 2X Driver. if (power == SDMMC_POWER_OFF) break; + u32 sdmmc1_pad_cfg = APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xF8080FFF; if (power == SDMMC_POWER_1_8) - APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x304; // Up: 3, Dn: 4. + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg | (0xB0F << 12); // Up: 11, Dn: 15. For 33 ohm. else if (power == SDMMC_POWER_3_3) - APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x808; // Up: 8, Dn: 8. + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg | (0xC0C << 12); // Up: 12, Dn: 12. For 33 ohm. break; - case SDMMC_4: - APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0x3FFC) | 0x1040; + case SDMMC_2: + case SDMMC_4: // 50 Ohm 2X Driver. PU:16, PD:16. + APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040; break; } - //TODO: load standard values for other controllers, can depend on power. } -static int _sdmmc_wait_type4(sdmmc_t *sdmmc) +static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power) { - int res = 1, should_disable_sd_clock = 0; - - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + bool should_enable_sd_clock = false; + if (sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN) { - should_disable_sd_clock = 1; - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + should_enable_sd_clock = true; + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; } - sdmmc->regs->vendllcal |= 0x80000000; - _sdmmc_get_clkcon(sdmmc); + // Enable E_INPUT power. + if (!(sdmmc->regs->sdmemcmppadctl & TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD)) + { + sdmmc->regs->sdmemcmppadctl |= TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD; + _sdmmc_get_clkcon(sdmmc); + usleep(1); + } - u32 timeout = get_tmr_ms() + 5; - while (sdmmc->regs->vendllcal & 0x80000000) + // Enable auto calibration and start auto configuration. + sdmmc->regs->autocalcfg |= TEGRA_MMC_AUTOCALCFG_AUTO_CAL_ENABLE | TEGRA_MMC_AUTOCALCFG_AUTO_CAL_START; + _sdmmc_get_clkcon(sdmmc); + usleep(2); + + u64 timeout = get_tmr_ms() + 10; + while (sdmmc->regs->autocalsts & TEGRA_MMC_AUTOCALSTS_AUTO_CAL_ACTIVE) { if (get_tmr_ms() > timeout) { - res = 0; + timeout = 0; // Set timeout to 0 if we timed out. + break; + } + } +/* + // Check if PU results are inside limits. + // SDMMC1: CZ pads - 7-bit PU. SDMMC2/4: LV_CZ pads - 5-bit PU. + u8 autocal_pu_status = sdmmc->regs->autocalsts & 0x7F; + switch (sdmmc->id) + { + case SDMMC_1: + if (!autocal_pu_status || autocal_pu_status == 0x7F) + timeout = 0; + break; + case SDMMC_2: + case SDMMC_4: + autocal_pu_status &= 0x1F; + if (!autocal_pu_status || autocal_pu_status == 0x1F) + timeout = 0; + break; + } +*/ + // In case auto calibration fails, we load suggested standard values. + if (!timeout) + { + _sdmmc_pad_config_fallback(sdmmc, power); + sdmmc->regs->autocalcfg &= ~TEGRA_MMC_AUTOCALCFG_AUTO_CAL_ENABLE; + } + + // Disable E_INPUT to conserve power. + sdmmc->regs->sdmemcmppadctl &= ~TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD; + + if(should_enable_sd_clock) + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; +} + +static int _sdmmc_dll_cal_execute(sdmmc_t *sdmmc) +{ + int result = 1, should_disable_sd_clock = 0; + + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; + } + + sdmmc->regs->vendllcalcfg |= TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE; + _sdmmc_get_clkcon(sdmmc); + + u64 timeout = get_tmr_ms() + 5; + while (sdmmc->regs->vendllcalcfg & TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE) + { + if (get_tmr_ms() > timeout) + { + result = 0; goto out; } } timeout = get_tmr_ms() + 10; - while (sdmmc->regs->dllcfgstatus & 0x80000000) + while (sdmmc->regs->vendllcalcfgsts & TEGRA_MMC_DLLCAL_CFG_STATUS_DLL_ACTIVE) { if (get_tmr_ms() > timeout) { - res = 0; + result = 0; goto out; } } out:; if (should_disable_sd_clock) - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - return res; + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; + return result; +} + +static void _sdmmc_reset(sdmmc_t *sdmmc) +{ + sdmmc->regs->swrst |= SDHCI_RESET_CMD | SDHCI_RESET_DATA; + _sdmmc_get_clkcon(sdmmc); + u64 timeout = get_tmr_ms() + 2000; + while ((sdmmc->regs->swrst & (SDHCI_RESET_CMD | SDHCI_RESET_DATA)) && get_tmr_ms() < timeout) + ; } int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type) { - //Disable the SD clock if it was enabled, and reenable it later. + // Disable the SD clock if it was enabled, and reenable it later. bool should_enable_sd_clock = false; - if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) + if (sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN) { should_enable_sd_clock = true; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; } - _sdmmc_config_ven_ceata_clk(sdmmc, type); + _sdmmc_config_tap_val(sdmmc, type); + + _sdmmc_reset(sdmmc); switch (type) { - case 0: - case 1: - case 5: - case 6: - sdmmc->regs->hostctl &= 0xFB; //Should this be 0xFFFB (~4) ? - sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + case SDHCI_TIMING_MMC_ID: + case SDHCI_TIMING_MMC_LS26: + case SDHCI_TIMING_SD_ID: + case SDHCI_TIMING_SD_DS12: + sdmmc->regs->hostctl &= ~SDHCI_CTRL_HISPD; + sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_VDD_180; break; - case 2: - case 7: - sdmmc->regs->hostctl |= 4; - sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + case SDHCI_TIMING_MMC_HS52: + case SDHCI_TIMING_SD_HS25: + sdmmc->regs->hostctl |= SDHCI_CTRL_HISPD; + sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_VDD_180; break; - case 3: - case 11: - case 13: - case 14: + case SDHCI_TIMING_MMC_HS200: + case SDHCI_TIMING_UHS_SDR50: // T210 Errata for SDR50, the host must be set to SDR104. + case SDHCI_TIMING_UHS_SDR104: + case SDHCI_TIMING_UHS_SDR82: + case SDHCI_TIMING_UHS_DDR50: + case SDHCI_TIMING_MMC_DDR52: sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; break; - case 4: - //Non standard + case SDHCI_TIMING_MMC_HS400: + // Non standard. sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | HS400_BUS_SPEED; sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; break; - case 8: - sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR12_BUS_SPEED; + case SDHCI_TIMING_UHS_SDR25: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR25_BUS_SPEED; sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; break; - case 10: - //T210 Errata for SDR50, the host must be set to SDR104. - sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; + case SDHCI_TIMING_UHS_SDR12: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR12_BUS_SPEED; sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; break; } _sdmmc_get_clkcon(sdmmc); - u32 tmp; + u32 clock; u16 divisor; - clock_sdmmc_get_params(&tmp, &divisor, type); - clock_sdmmc_config_clock_source(&tmp, sdmmc->id, tmp); - sdmmc->divisor = (tmp + divisor - 1) / divisor; + clock_sdmmc_get_card_clock_div(&clock, &divisor, type); + clock_sdmmc_config_clock_source(&clock, sdmmc->id, clock); + sdmmc->divisor = (clock + divisor - 1) / divisor; //if divisor != 1 && divisor << 31 -> error u16 div = divisor >> 1; divisor = 0; if (div > 0xFF) - divisor = div >> 8; - sdmmc->regs->clkcon = (sdmmc->regs->clkcon & 0x3F) | (div << 8) | (divisor << 6); + divisor = div >> SDHCI_DIVIDER_SHIFT; - //Enable the SD clock again. + sdmmc->regs->clkcon = (sdmmc->regs->clkcon & ~(SDHCI_DIV_MASK | SDHCI_DIV_HI_MASK)) + | (div << SDHCI_DIVIDER_SHIFT) | (divisor << SDHCI_DIVIDER_HI_SHIFT); + + // Enable the SD clock again. if (should_enable_sd_clock) - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; - if (type == 4) - return _sdmmc_wait_type4(sdmmc); + if (type == SDHCI_TIMING_MMC_HS400) + return _sdmmc_dll_cal_execute(sdmmc); return 1; } -static void _sdmmc_sd_clock_enable(sdmmc_t *sdmmc) +static void _sdmmc_card_clock_enable(sdmmc_t *sdmmc) { - if (!sdmmc->no_sd) + // Recalibrate conditionally. + if ((sdmmc->id == SDMMC_1) && !sdmmc->auto_cal_enabled) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc)); + + if (!sdmmc->auto_cal_enabled) { - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; } - sdmmc->sd_clock_enabled = 1; + sdmmc->card_clock_enabled = 1; } static void _sdmmc_sd_clock_disable(sdmmc_t *sdmmc) { - sdmmc->sd_clock_enabled = 0; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->card_clock_enabled = 0; + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; } -void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd) +void sdmmc_card_clock_ctrl(sdmmc_t *sdmmc, int auto_cal_enable) { - sdmmc->no_sd = no_sd; - if (no_sd) + // Recalibrate periodically for SDMMC1. + if ((sdmmc->id == SDMMC_1) && !auto_cal_enable && sdmmc->card_clock_enabled) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc)); + + sdmmc->auto_cal_enabled = auto_cal_enable; + if (auto_cal_enable) { - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) return; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; return; } - if (sdmmc->sd_clock_enabled) - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + if (sdmmc->card_clock_enabled) + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; } static int _sdmmc_cache_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) @@ -381,22 +465,12 @@ int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) return 1; } -static void _sdmmc_reset(sdmmc_t *sdmmc) -{ - sdmmc->regs->swrst |= - TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE | TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE; - _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_ms() + 2000; - while (sdmmc->regs->swrst << 29 >> 30 && get_tmr_ms() < timeout) - ; -} - -static int _sdmmc_wait_prnsts_type0(sdmmc_t *sdmmc, u32 wait_dat) +static int _sdmmc_wait_cmd_data_inhibit(sdmmc_t *sdmmc, bool wait_dat) { _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_ms() + 2000; - while(sdmmc->regs->prnsts & 1) //CMD inhibit. + u64 timeout = get_tmr_ms() + 2000; + while(sdmmc->regs->prnsts & SDHCI_CMD_INHIBIT) if (get_tmr_ms() > timeout) { _sdmmc_reset(sdmmc); @@ -406,7 +480,7 @@ static int _sdmmc_wait_prnsts_type0(sdmmc_t *sdmmc, u32 wait_dat) if (wait_dat) { timeout = get_tmr_ms() + 2000; - while (sdmmc->regs->prnsts & 2) //DAT inhibit. + while (sdmmc->regs->prnsts & SDHCI_DATA_INHIBIT) if (get_tmr_ms() > timeout) { _sdmmc_reset(sdmmc); @@ -417,12 +491,12 @@ static int _sdmmc_wait_prnsts_type0(sdmmc_t *sdmmc, u32 wait_dat) return 1; } -static int _sdmmc_wait_prnsts_type1(sdmmc_t *sdmmc) +static int _sdmmc_wait_card_busy(sdmmc_t *sdmmc) { _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_ms() + 2000; - while (!(sdmmc->regs->prnsts & 0x100000)) //DAT0 line level. + u64 timeout = get_tmr_ms() + 2000; + while (!(sdmmc->regs->prnsts & SDHCI_DATA_0_LVL_MASK)) if (get_tmr_ms() > timeout) { _sdmmc_reset(sdmmc); @@ -440,21 +514,21 @@ static int _sdmmc_setup_read_small_block(sdmmc_t *sdmmc) return 0; break; case SDMMC_BUS_WIDTH_4: - sdmmc->regs->blksize = 0x40; + sdmmc->regs->blksize = 64; break; case SDMMC_BUS_WIDTH_8: - sdmmc->regs->blksize = 0x80; + sdmmc->regs->blksize = 128; break; } sdmmc->regs->blkcnt = 1; - sdmmc->regs->trnmod = TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + sdmmc->regs->trnmod = SDHCI_TRNS_READ; return 1; } -static int _sdmmc_parse_cmdbuf(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_present) +static int _sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_present) { u16 cmdflags = 0; - + switch (cmd->rsp_type) { case SDMMC_RSP_TYPE_0: @@ -463,20 +537,15 @@ static int _sdmmc_parse_cmdbuf(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_pr case SDMMC_RSP_TYPE_4: case SDMMC_RSP_TYPE_5: if (cmd->check_busy) - cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY | - TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | - TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + cmdflags = SDHCI_CMD_RESP_LEN48_BUSY | SDHCI_CMD_INDEX | SDHCI_CMD_CRC; else - cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 | - TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | - TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + cmdflags = SDHCI_CMD_RESP_LEN48 | SDHCI_CMD_INDEX | SDHCI_CMD_CRC; break; case SDMMC_RSP_TYPE_2: - cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 | - TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + cmdflags = SDHCI_CMD_RESP_LEN136 | SDHCI_CMD_CRC; break; case SDMMC_RSP_TYPE_3: - cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + cmdflags = SDHCI_CMD_RESP_LEN48; break; default: return 0; @@ -484,121 +553,134 @@ static int _sdmmc_parse_cmdbuf(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_pr } if (is_data_present) - cmdflags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + cmdflags |= SDHCI_CMD_DATA; sdmmc->regs->argument = cmd->arg; sdmmc->regs->cmdreg = (cmd->cmd << 8) | cmdflags; return 1; } -static void _sdmmc_parse_cmd_48(sdmmc_t *sdmmc, u32 cmd) +static void _sdmmc_send_tuning_cmd(sdmmc_t *sdmmc, u32 cmd) { sdmmc_cmd_t cmdbuf; cmdbuf.cmd = cmd; cmdbuf.arg = 0; cmdbuf.rsp_type = SDMMC_RSP_TYPE_1; cmdbuf.check_busy = 0; - _sdmmc_parse_cmdbuf(sdmmc, &cmdbuf, true); + _sdmmc_send_cmd(sdmmc, &cmdbuf, true); } -static int _sdmmc_config_tuning_once(sdmmc_t *sdmmc, u32 cmd) +static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd) { - if (sdmmc->no_sd) + if (sdmmc->auto_cal_enabled) return 0; - if (!_sdmmc_wait_prnsts_type0(sdmmc, 1)) + if (!_sdmmc_wait_cmd_data_inhibit(sdmmc, true)) return 0; _sdmmc_setup_read_small_block(sdmmc); - sdmmc->regs->norintstsen |= TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY; + + sdmmc->regs->norintstsen |= SDHCI_INT_DATA_AVAIL; sdmmc->regs->norintsts = sdmmc->regs->norintsts; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - _sdmmc_parse_cmd_48(sdmmc, cmd); + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; + + _sdmmc_send_tuning_cmd(sdmmc, cmd); _sdmmc_get_clkcon(sdmmc); usleep(1); + _sdmmc_reset(sdmmc); - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_us() + 5000; + u64 timeout = get_tmr_us() + 5000; while (get_tmr_us() < timeout) { - if (sdmmc->regs->norintsts & 0x20) + if (sdmmc->regs->norintsts & SDHCI_INT_DATA_AVAIL) { - sdmmc->regs->norintsts = 0x20; - sdmmc->regs->norintstsen &= 0xFFDF; + sdmmc->regs->norintsts = SDHCI_INT_DATA_AVAIL; + sdmmc->regs->norintstsen &= ~SDHCI_INT_DATA_AVAIL; _sdmmc_get_clkcon(sdmmc); usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); return 1; } } + _sdmmc_reset(sdmmc); - sdmmc->regs->norintstsen &= 0xFFDF; + + sdmmc->regs->norintstsen &= ~SDHCI_INT_DATA_AVAIL; _sdmmc_get_clkcon(sdmmc); usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); + return 0; } -int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd) +int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd) { u32 max = 0, flag = 0; - sdmmc->regs->field_1C4 = 0; switch (type) { - case 3: - case 4: - case 11: - max = 0x80; - flag = 0x4000; + case SDHCI_TIMING_MMC_HS200: + case SDHCI_TIMING_MMC_HS400: + case SDHCI_TIMING_UHS_SDR104: + case SDHCI_TIMING_UHS_SDR82: + max = 128; + flag = (2 << 13); // 128 iterations. break; - case 10: - case 13: - case 14: - max = 0x100; - flag = 0x8000; + case SDHCI_TIMING_UHS_SDR50: + case SDHCI_TIMING_UHS_DDR50: + case SDHCI_TIMING_MMC_DDR52: + max = 256; + flag = (4 << 13); // 256 iterations. break; + case SDHCI_TIMING_UHS_SDR12: + case SDHCI_TIMING_UHS_SDR25: + return 1; default: return 0; } - sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFF1FFF) | flag; - sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFFE03F) | 0x40; - sdmmc->regs->ventunctl0 |= 0x20000; - sdmmc->regs->hostctl2 |= SDHCI_CTRL_EXEC_TUNING; + sdmmc->regs->ventunctl1 = 0; // step_size 1. + + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFF1FFF) | flag; // Tries. + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFFE03F) | (1 << 6); // 1x Multiplier. + sdmmc->regs->ventunctl0 |= TEGRA_MMC_VNDR_TUN_CTRL0_TAP_VAL_UPDATED_BY_HW; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_EXEC_TUNING; for (u32 i = 0; i < max; i++) { - _sdmmc_config_tuning_once(sdmmc, cmd); + _sdmmc_tuning_execute_once(sdmmc, cmd); if (!(sdmmc->regs->hostctl2 & SDHCI_CTRL_EXEC_TUNING)) break; } if (sdmmc->regs->hostctl2 & SDHCI_CTRL_TUNED_CLK) return 1; + return 0; } static int _sdmmc_enable_internal_clock(sdmmc_t *sdmmc) { //Enable internal clock and wait till it is stable. - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE; + sdmmc->regs->clkcon |= SDHCI_CLOCK_INT_EN; _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_ms() + 2000; - while (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) + u64 timeout = get_tmr_ms() + 2000; + while (!(sdmmc->regs->clkcon & SDHCI_CLOCK_INT_STABLE)) { if (get_tmr_ms() > timeout) return 0; } sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_PRESET_VAL_EN; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_CLKGEN_SELECT; + sdmmc->regs->clkcon &= ~SDHCI_PROG_CLOCK_MODE; sdmmc->regs->hostctl2 |= SDHCI_HOST_VERSION_4_EN; - if (!(sdmmc->regs->capareg & 0x10000000)) + if (!(sdmmc->regs->capareg & SDHCI_CAN_64BIT)) return 0; sdmmc->regs->hostctl2 |= SDHCI_ADDRESSING_64BIT_EN; - sdmmc->regs->hostctl &= 0xE7; + sdmmc->regs->hostctl &= ~SDHCI_CTRL_DMA_MASK; sdmmc->regs->timeoutcon = (sdmmc->regs->timeoutcon & 0xF0) | 0xE; return 1; @@ -635,60 +717,22 @@ static int _sdmmc_autocal_config_offset(sdmmc_t *sdmmc, u32 power) break; } - sdmmc->regs->autocalcfg = (((sdmmc->regs->autocalcfg & 0xFFFF80FF) | (off_pd << 8)) >> 7 << 7) | off_pu; + sdmmc->regs->autocalcfg = (sdmmc->regs->autocalcfg & 0xFFFF8080) | (off_pd << 8) | off_pu; return 1; } -static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power) -{ - bool should_enable_sd_clock = false; - if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) - { - should_enable_sd_clock = true; - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - } - - if (!(sdmmc->regs->sdmemcmppadctl & 0x80000000)) - { - sdmmc->regs->sdmemcmppadctl |= 0x80000000; - _sdmmc_get_clkcon(sdmmc); - usleep(1); - } - - sdmmc->regs->autocalcfg |= 0xA0000000; - _sdmmc_get_clkcon(sdmmc); - usleep(1); - - u32 timeout = get_tmr_ms() + 10; - while (sdmmc->regs->autocalcfg & 0x80000000) - { - if (get_tmr_ms() > timeout) - { - //In case autocalibration fails, we load suggested standard values. - _sdmmc_pad_config_fallback(sdmmc, power); - sdmmc->regs->autocalcfg &= 0xDFFFFFFF; - break; - } - } - - sdmmc->regs->sdmemcmppadctl &= 0x7FFFFFFF; - - if(should_enable_sd_clock) - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; -} - static void _sdmmc_enable_interrupts(sdmmc_t *sdmmc) { - sdmmc->regs->norintstsen |= 0xB; - sdmmc->regs->errintstsen |= 0x17F; + sdmmc->regs->norintstsen |= SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; + sdmmc->regs->errintstsen |= SDHCI_ERR_INT_ALL_EXCEPT_ADMA_BUSPWR; sdmmc->regs->norintsts = sdmmc->regs->norintsts; sdmmc->regs->errintsts = sdmmc->regs->errintsts; } static void _sdmmc_mask_interrupts(sdmmc_t *sdmmc) { - sdmmc->regs->errintstsen &= 0xFE80; - sdmmc->regs->norintstsen &= 0xFFF4; + sdmmc->regs->errintstsen &= ~SDHCI_ERR_INT_ALL_EXCEPT_ADMA_BUSPWR; + sdmmc->regs->norintstsen &= ~(SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); } static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask) @@ -696,13 +740,13 @@ static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask) u16 norintsts = sdmmc->regs->norintsts; u16 errintsts = sdmmc->regs->errintsts; - DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts); +DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts); if (pout) *pout = norintsts; - //Check for error interrupt. - if (norintsts & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + // Check for error interrupt. + if (norintsts & SDHCI_INT_ERROR) { sdmmc->regs->errintsts = errintsts; return SDMMC_MASKINT_ERROR; @@ -712,21 +756,21 @@ static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask) sdmmc->regs->norintsts = norintsts & mask; return SDMMC_MASKINT_MASKED; } - + return SDMMC_MASKINT_NOERROR; } -static int _sdmmc_wait_request(sdmmc_t *sdmmc) +static int _sdmmc_wait_response(sdmmc_t *sdmmc) { _sdmmc_get_clkcon(sdmmc); - u32 timeout = get_tmr_ms() + 2000; - while (1) + u64 timeout = get_tmr_ms() + 2000; + while (true) { - int res = _sdmmc_check_mask_interrupt(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); - if (res == SDMMC_MASKINT_MASKED) + int result = _sdmmc_check_mask_interrupt(sdmmc, NULL, SDHCI_INT_RESPONSE); + if (result == SDMMC_MASKINT_MASKED) break; - if (res != SDMMC_MASKINT_NOERROR || get_tmr_ms() > timeout) + if (result != SDMMC_MASKINT_NOERROR || get_tmr_ms() > timeout) { _sdmmc_reset(sdmmc); return 0; @@ -740,45 +784,54 @@ static int _sdmmc_stop_transmission_inner(sdmmc_t *sdmmc, u32 *rsp) { sdmmc_cmd_t cmd; - if (!_sdmmc_wait_prnsts_type0(sdmmc, 0)) + if (!_sdmmc_wait_cmd_data_inhibit(sdmmc, false)) return 0; _sdmmc_enable_interrupts(sdmmc); + cmd.cmd = MMC_STOP_TRANSMISSION; cmd.arg = 0; cmd.rsp_type = SDMMC_RSP_TYPE_1; cmd.check_busy = 1; - _sdmmc_parse_cmdbuf(sdmmc, &cmd, false); - int res = _sdmmc_wait_request(sdmmc); + + _sdmmc_send_cmd(sdmmc, &cmd, false); + + int result = _sdmmc_wait_response(sdmmc); _sdmmc_mask_interrupts(sdmmc); - if (!res) + if (!result) return 0; - + _sdmmc_cache_rsp(sdmmc, rsp, 4, SDMMC_RSP_TYPE_1); - return _sdmmc_wait_prnsts_type1(sdmmc); + + return _sdmmc_wait_card_busy(sdmmc); } int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp) { - if (!sdmmc->sd_clock_enabled) + if (!sdmmc->card_clock_enabled) return 0; + // Recalibrate periodically for SDMMC1. + if ((sdmmc->id == SDMMC_1) && sdmmc->auto_cal_enabled) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc)); + bool should_disable_sd_clock = false; - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) { should_disable_sd_clock = true; - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; _sdmmc_get_clkcon(sdmmc); usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); } - int res = _sdmmc_stop_transmission_inner(sdmmc, rsp); + int result = _sdmmc_stop_transmission_inner(sdmmc, rsp); usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); - if (should_disable_sd_clock) - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - return res; + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; + + return result; } static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req) @@ -799,7 +852,7 @@ static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req) sdmmc->last_dma_idx = dma_idx; } - //Check alignment. + // Check alignment. if (admaaddr & 7) return 0; @@ -814,15 +867,13 @@ static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req) if (blkcnt_out) *blkcnt_out = blkcnt; - u32 trnmode = TEGRA_MMC_TRNMOD_DMA_ENABLE; + u32 trnmode = SDHCI_TRNS_DMA; if (req->is_multi_block) - trnmode = TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT | - TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE | - TEGRA_MMC_TRNMOD_DMA_ENABLE; + trnmode = SDHCI_TRNS_MULTI | SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_DMA; if (!req->is_write) - trnmode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + trnmode |= SDHCI_TRNS_READ; if (req->is_auto_cmd12) - trnmode = (trnmode & 0xFFF3) | TEGRA_MMC_TRNMOD_AUTO_CMD12; + trnmode = (trnmode & ~(SDHCI_TRNS_AUTO_CMD12 | SDHCI_TRNS_AUTO_CMD23)) | SDHCI_TRNS_AUTO_CMD12; sdmmc->regs->trnmod = trnmode; @@ -835,28 +886,30 @@ static int _sdmmc_update_dma(sdmmc_t *sdmmc) do { blkcnt = sdmmc->regs->blkcnt; - u32 timeout = get_tmr_ms() + 1500; + u64 timeout = get_tmr_ms() + 1500; do { - int res = 0; - while (1) + int result = 0; + while (true) { u16 intr = 0; - res = _sdmmc_check_mask_interrupt(sdmmc, &intr, - TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); - if (res < 0) + result = _sdmmc_check_mask_interrupt(sdmmc, &intr, + SDHCI_INT_DATA_END | SDHCI_INT_DMA_END); + if (result < 0) break; - if (intr & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) - return 1; //Transfer complete. - if (intr & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) + + if (intr & SDHCI_INT_DATA_END) + return 1; // Transfer complete. + + if (intr & SDHCI_INT_DMA_END) { - //Update DMA. + // Update DMA. sdmmc->regs->admaaddr = sdmmc->dma_addr_next & 0xFFFFFFFFF; sdmmc->regs->admaaddr_hi = (sdmmc->dma_addr_next >> 32) & 0xFFFFFFFFF; sdmmc->dma_addr_next += 0x80000; } } - if (res != SDMMC_MASKINT_NOERROR) + if (result != SDMMC_MASKINT_NOERROR) { _sdmmc_reset(sdmmc); return 0; @@ -871,14 +924,16 @@ static int _sdmmc_update_dma(sdmmc_t *sdmmc) static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) { int has_req_or_check_busy = req || cmd->check_busy; - if (!_sdmmc_wait_prnsts_type0(sdmmc, has_req_or_check_busy)) + if (!_sdmmc_wait_cmd_data_inhibit(sdmmc, has_req_or_check_busy)) return 0; u32 blkcnt = 0; bool is_data_present = false; if (req) { - _sdmmc_config_dma(sdmmc, &blkcnt, req); + if (!_sdmmc_config_dma(sdmmc, &blkcnt, req)) + return 0; + if(!sdmmc_memcpy_buf) { // Flush from/to phys @@ -896,41 +951,40 @@ static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_ } } - _sdmmc_enable_interrupts(sdmmc); is_data_present = true; } else - { - _sdmmc_enable_interrupts(sdmmc); is_data_present = false; - } - _sdmmc_parse_cmdbuf(sdmmc, cmd, is_data_present); + _sdmmc_enable_interrupts(sdmmc); - int res = _sdmmc_wait_request(sdmmc); - DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", res, + if (!_sdmmc_send_cmd(sdmmc, cmd, is_data_present)) + return 0; + + int result = _sdmmc_wait_response(sdmmc); + DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", result, sdmmc->regs->rspreg0, sdmmc->regs->rspreg1, sdmmc->regs->rspreg2, sdmmc->regs->rspreg3); - if (res) + if (result) { if (cmd->rsp_type) { sdmmc->expected_rsp_type = cmd->rsp_type; - _sdmmc_cache_rsp(sdmmc, sdmmc->rsp, 0x10, cmd->rsp_type); - + result = _sdmmc_cache_rsp(sdmmc, sdmmc->rsp, 0x10, cmd->rsp_type); + /*if(sdmmc->rsp[0] & 0xFDF9A080) { res = 0; sdmmc->rsp[0] = 0; // Reset error }*/ } - - if (res && req) - _sdmmc_update_dma(sdmmc); + + if (req && result) + result = _sdmmc_update_dma(sdmmc); } _sdmmc_mask_interrupts(sdmmc); - if (res) + if (result) { if (req) { @@ -959,34 +1013,46 @@ static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_ } if (cmd->check_busy || req) - return _sdmmc_wait_prnsts_type1(sdmmc); + return _sdmmc_wait_card_busy(sdmmc); } - return res; + return result; +} + +int sdmmc_get_sd_power_enabled() +{ + return gpio_read(GPIO_PORT_E, GPIO_PIN_4); +} + +bool sdmmc_get_sd_inserted() +{ + return (!gpio_read(GPIO_PORT_Z, GPIO_PIN_1)); } static int _sdmmc_config_sdmmc1() { - //Configure SD card detect. - PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 1; //GPIO control, pull up. + // Configure SD card detect. + PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 2; // GPIO control, pull up. APB_MISC(APB_MISC_GP_VGPIO_GPIO_MUX_SEL) = 0; gpio_config(GPIO_PORT_Z, GPIO_PIN_1, GPIO_MODE_GPIO); gpio_output_enable(GPIO_PORT_Z, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); usleep(100); - if(!!gpio_read(GPIO_PORT_Z, GPIO_PIN_1)) + + // Check if SD card is inserted. + if(!sdmmc_get_sd_inserted()) return 0; /* * Pinmux config: * DRV_TYPE = DRIVE_2X - * E_SCHMT = ENABLE (for 1.8V), DISABLE (for 3.3V) - * E_INPUT = ENABLE + * E_SCHMT = ENABLE (for 1.8V), DISABLE (for 3.3V) + * E_INPUT = ENABLE * TRISTATE = PASSTHROUGH * APB_MISC_GP_SDMMCx_CLK_LPBK_CONTROL = SDMMCx_CLK_PAD_E_LPBK for CLK */ - //Configure SDMMC1 pinmux. - APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1; + // Configure SDMMC1 pinmux. + APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1; // Enable deep loopback for SDMMC1 CLK pad. PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED; PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; @@ -994,51 +1060,81 @@ static int _sdmmc_config_sdmmc1() PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; - //Make sure SDMMC1 controller is reset. - smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, (1 << 12), (1 << 12)); + // Make sure the SDMMC1 controller is powered. + smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, PMC_NO_IOPOWER_SDMMC1_IO_EN, PMC_NO_IOPOWER_SDMMC1_IO_EN); usleep(1000); + smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, ~PMC_NO_IOPOWER_SDMMC1_IO_EN, PMC_NO_IOPOWER_SDMMC1_IO_EN); - //Make sure the SDMMC1 controller is powered. - smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, ~(1 << 12), (1 << 12)); - smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, (1 << 12), (1 << 12)); + // Inform IO pads that voltage is gonna be 3.3V. + smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN); - //Set enable SD card power. - PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN | 1; //GPIO control, pull down. + // Set enable SD card power. + //PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_PULL_DOWN | 2; // Pull down. + PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN | 1; // GPIO control, pull down. gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO); gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH); gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE); - usleep(1000); - //Enable SD card power. + // Enable SD card power. max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000); max77620_regulator_enable(REGULATOR_LDO2, 1); - usleep(1000); - //For good measure. - APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x10000000; - + // Set pad slew codes to get good quality clock. + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xFFFFFFF) | 0x50000000; usleep(1000); return 1; } -int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd) +static void _sdmmc_config_emmc(u32 id) { + switch (id) + { + case SDMMC_2: + // Unset park for pads. + APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) &= 0xF8003FFF; + break; + case SDMMC_4: + // Unset park for pads. + APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) &= 0xF8003FFF; + // Set default pad cfg. + APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040; + + // Enabled schmitt trigger. + APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) |= 1; // Enable Schmitt trigger. + break; + } + +} + +int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int auto_cal_enable) +{ + const u32 trim_values[] = { 2, 8, 3, 8 }; + if (id > SDMMC_4) return 0; - if (id == SDMMC_1) - if (!_sdmmc_config_sdmmc1()) - return 0; - memset(sdmmc, 0, sizeof(sdmmc_t)); sdmmc->regs = (t210_sdmmc_t *)QueryIoMapping(_sdmmc_bases[id], 0x200); sdmmc->id = id; sdmmc->clock_stopped = 1; + // Do specific SDMMC HW configuration. + switch (id) + { + case SDMMC_1: + if (!_sdmmc_config_sdmmc1()) + return 0; + break; + case SDMMC_2: + case SDMMC_4: + _sdmmc_config_emmc(id); + break; + } + if (clock_sdmmc_is_not_reset_and_enabled(id)) { _sdmmc_sd_clock_disable(sdmmc); @@ -1047,31 +1143,37 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int n u32 clock; u16 divisor; - clock_sdmmc_get_params(&clock, &divisor, type); + clock_sdmmc_get_card_clock_div(&clock, &divisor, type); clock_sdmmc_enable(id, clock); sdmmc->clock_stopped = 0; //TODO: make this skip-able. - sdmmc->regs->iospare |= 0x80000; - sdmmc->regs->veniotrimctl &= 0xFFFFFFFB; - static const u32 trim_values[] = { 2, 8, 3, 8 }; - sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFF) | (trim_values[sdmmc->id] << 24); - sdmmc->regs->sdmemcmppadctl = (sdmmc->regs->sdmemcmppadctl & 0xFFFFFFF0) | 7; + sdmmc->regs->iospare |= 0x80000; // Enable muxing. + sdmmc->regs->veniotrimctl &= 0xFFFFFFFB; // Set Band Gap VREG to supply DLL. + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFB) | (trim_values[sdmmc->id] << 24); + sdmmc->regs->sdmemcmppadctl = + (sdmmc->regs->sdmemcmppadctl & TEGRA_MMC_SDMEMCOMPPADCTRL_COMP_VREF_SEL_MASK) | 7; + if (!_sdmmc_autocal_config_offset(sdmmc, power)) return 0; + _sdmmc_autocal_execute(sdmmc, power); + if (_sdmmc_enable_internal_clock(sdmmc)) { sdmmc_set_bus_width(sdmmc, bus_width); - _sdmmc_set_voltage(sdmmc, power); + _sdmmc_set_io_power(sdmmc, power); + if (sdmmc_setup_clock(sdmmc, type)) { - sdmmc_sd_clock_ctrl(sdmmc, no_sd); - _sdmmc_sd_clock_enable(sdmmc); + sdmmc_card_clock_ctrl(sdmmc, auto_cal_enable); + _sdmmc_card_clock_enable(sdmmc); _sdmmc_get_clkcon(sdmmc); + return 1; } + return 0; } return 0; @@ -1082,16 +1184,19 @@ void sdmmc_end(sdmmc_t *sdmmc) if (!sdmmc->clock_stopped) { _sdmmc_sd_clock_disable(sdmmc); - // Disable SDMMC power. - _sdmmc_set_voltage(sdmmc, SDMMC_POWER_OFF); + // Disable SDMMC power. + _sdmmc_set_io_power(sdmmc, SDMMC_POWER_OFF); // Disable SD card power. if (sdmmc->id == SDMMC_1) { gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_DISABLE); - msleep(1); // To power cycle min 1ms without power is needed. max77620_regulator_enable(REGULATOR_LDO2, 0); - msleep(100); // Some extra. + + // Inform IO pads that next voltage might be 3.3V. + smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN); + + msleep(100); // To power cycle min 1ms without power is needed. } _sdmmc_get_clkcon(sdmmc); @@ -1110,28 +1215,29 @@ void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 che int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) { - if (!sdmmc->sd_clock_enabled) + if (!sdmmc->card_clock_enabled) return 0; - //Recalibrate periodically for SDMMC1. - if (sdmmc->id == SDMMC_1 && sdmmc->no_sd) - _sdmmc_autocal_execute(sdmmc, sdmmc_get_voltage(sdmmc)); + // Recalibrate periodically for SDMMC1. + if (sdmmc->id == SDMMC_1 && sdmmc->auto_cal_enabled) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc)); int should_disable_sd_clock = 0; - if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)) { should_disable_sd_clock = 1; - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; _sdmmc_get_clkcon(sdmmc); usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); } - int res = _sdmmc_execute_cmd_inner(sdmmc, cmd, req, blkcnt_out); + int result = _sdmmc_execute_cmd_inner(sdmmc, cmd, req, blkcnt_out); usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); - if (should_disable_sd_clock) - sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - return res; + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; + + return result; } int sdmmc_enable_low_voltage(sdmmc_t *sdmmc) @@ -1139,25 +1245,38 @@ int sdmmc_enable_low_voltage(sdmmc_t *sdmmc) if(sdmmc->id != SDMMC_1) return 0; - if (!sdmmc_setup_clock(sdmmc, 8)) + if (!sdmmc_setup_clock(sdmmc, SDHCI_TIMING_UHS_SDR12)) return 0; _sdmmc_get_clkcon(sdmmc); + // Switch to 1.8V and wait for regulator to stabilize. Assume max possible wait needed. max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000); - smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, ~(1 << 12), (1 << 12)); + usleep(300); + + // Inform IO pads that we switched to 1.8V. + smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, ~PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN); + + // Enable schmitt trigger for better duty cycle and low jitter clock. + PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) |= PINMUX_SCHMT; + PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) |= PINMUX_SCHMT; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) |= PINMUX_SCHMT; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) |= PINMUX_SCHMT; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) |= PINMUX_SCHMT; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) |= PINMUX_SCHMT; _sdmmc_autocal_config_offset(sdmmc, SDMMC_POWER_1_8); _sdmmc_autocal_execute(sdmmc, SDMMC_POWER_1_8); - _sdmmc_set_voltage(sdmmc, SDMMC_POWER_1_8); + _sdmmc_set_io_power(sdmmc, SDMMC_POWER_1_8); _sdmmc_get_clkcon(sdmmc); - msleep(5); - + msleep(5); // Wait minimum 5ms before turning on the card clock. + + // Turn on SDCLK. if (sdmmc->regs->hostctl2 & SDHCI_CTRL_VDD_180) { - sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; _sdmmc_get_clkcon(sdmmc); - msleep(1); + usleep(1000); if ((sdmmc->regs->prnsts & 0xF00000) == 0xF00000) return 1; } diff --git a/emummc/source/emmc/sdmmc_driver.h b/emummc/source/emmc/sdmmc_driver.h index d5c36d7c5..a19602528 100644 --- a/emummc/source/emmc/sdmmc_driver.h +++ b/emummc/source/emmc/sdmmc_driver.h @@ -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 diff --git a/emummc/source/emmc/sdmmc_t210.h b/emummc/source/emmc/sdmmc_t210.h index e11c3ff9c..eb17714ce 100644 --- a/emummc/source/emmc/sdmmc_t210.h +++ b/emummc/source/emmc/sdmmc_t210.h @@ -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 diff --git a/emummc/source/emuMMC/emummc.c b/emummc/source/emuMMC/emummc.c index ac4050a4e..237ebf116 100644 --- a/emummc/source/emuMMC/emummc.c +++ b/emummc/source/emuMMC/emummc.c @@ -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. diff --git a/emummc/source/emuMMC/emummc.h b/emummc/source/emuMMC/emummc.h index 16b1151e4..0acee8e4c 100644 --- a/emummc/source/emuMMC/emummc.h +++ b/emummc/source/emuMMC/emummc.h @@ -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 diff --git a/emummc/source/emuMMC/emummc_ctx.h b/emummc/source/emuMMC/emummc_ctx.h index 0bf404a3c..2663f528d 100644 --- a/emummc/source/emuMMC/emummc_ctx.h +++ b/emummc/source/emuMMC/emummc_ctx.h @@ -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; diff --git a/emummc/source/libs/fatfs/ff.c b/emummc/source/libs/fatfs/ff.c index 32304742f..50105173e 100644 --- a/emummc/source/libs/fatfs/ff.c +++ b/emummc/source/libs/fatfs/ff.c @@ -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; diff --git a/emummc/source/libs/fatfs/ff.h b/emummc/source/libs/fatfs/ff.h index 27bf89dbd..68dc7ab65 100644 --- a/emummc/source/libs/fatfs/ff.h +++ b/emummc/source/libs/fatfs/ff.h @@ -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 */ diff --git a/emummc/source/libs/fatfs/ffconf.h b/emummc/source/libs/fatfs/ffconf.h index a8be71b0a..3d01d8b0c 100644 --- a/emummc/source/libs/fatfs/ffconf.h +++ b/emummc/source/libs/fatfs/ffconf.h @@ -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 diff --git a/emummc/source/libs/fatfs/ffunicode.c b/emummc/source/libs/fatfs/ffunicode.c index bc23f806c..9f039637b 100644 --- a/emummc/source/libs/fatfs/ffunicode.c +++ b/emummc/source/libs/fatfs/ffunicode.c @@ -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 */ diff --git a/emummc/source/power/max77620.h b/emummc/source/power/max77620.h index fcce30980..26ea85595 100644 --- a/emummc/source/power/max77620.h +++ b/emummc/source/power/max77620.h @@ -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) diff --git a/emummc/source/power/max7762x.c b/emummc/source/power/max7762x.c index 2c8cff402..1e090bf52 100644 --- a/emummc/source/power/max7762x.c +++ b/emummc/source/power/max7762x.c @@ -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); -} diff --git a/emummc/source/power/max7762x.h b/emummc/source/power/max7762x.h index eefa11295..c8b5c5309 100644 --- a/emummc/source/power/max7762x.h +++ b/emummc/source/power/max7762x.h @@ -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 diff --git a/emummc/source/soc/clock.c b/emummc/source/soc/clock.c index ea1920865..d0d97cc4a 100644 --- a/emummc/source/soc/clock.c +++ b/emummc/source/soc/clock.c @@ -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); } diff --git a/emummc/source/soc/clock.h b/emummc/source/soc/clock.h index bad138bbe..09f18a0f1 100644 --- a/emummc/source/soc/clock.h +++ b/emummc/source/soc/clock.h @@ -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); diff --git a/emummc/source/soc/i2c.c b/emummc/source/soc/i2c.c index 3343f1d35..423210f6f 100644 --- a/emummc/source/soc/i2c.c +++ b/emummc/source/soc/i2c.c @@ -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; diff --git a/emummc/source/soc/pmc.h b/emummc/source/soc/pmc.h index ec8eb99a8..8cd916160 100644 --- a/emummc/source/soc/pmc.h +++ b/emummc/source/soc/pmc.h @@ -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 diff --git a/emummc/source/soc/t210.h b/emummc/source/soc/t210.h index 0ce1d5d39..8e6f3cfa9 100644 --- a/emummc/source/soc/t210.h +++ b/emummc/source/soc/t210.h @@ -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 diff --git a/emummc/source/utils/fatal.h b/emummc/source/utils/fatal.h index f2d42fd6f..7843b54d4 100644 --- a/emummc/source/utils/fatal.h +++ b/emummc/source/utils/fatal.h @@ -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 }; diff --git a/emummc/source/utils/util.c b/emummc/source/utils/util.c index 274be7e4f..01c96ae34 100644 --- a/emummc/source/utils/util.c +++ b/emummc/source/utils/util.c @@ -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); } diff --git a/emummc/source/utils/util.h b/emummc/source/utils/util.h index 843e969aa..bf13c8958 100644 --- a/emummc/source/utils/util.h +++ b/emummc/source/utils/util.h @@ -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 { From ff87ff259250a807044d852699926371dde51847 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Mon, 8 Jun 2020 18:11:16 -0700 Subject: [PATCH 089/118] emummc: update for exo2 --- emummc/emummc.json | 2 + emummc/source/main.c | 120 ++++++++++++++++++++++++++++++++++-- emummc/source/nx/smc.c | 76 +++++++++++------------ emummc/source/nx/smc.h | 8 +-- emummc/source/nx/svc.h | 98 +++++++++++++++++++++++++++++ emummc/source/nx/svc.s | 63 +++++++++++++++++++ emummc/source/utils/types.h | 4 ++ emummc/source/utils/util.h | 6 ++ 8 files changed, 328 insertions(+), 49 deletions(-) diff --git a/emummc/emummc.json b/emummc/emummc.json index 6cb2d5c90..19471c53d 100644 --- a/emummc/emummc.json +++ b/emummc/emummc.json @@ -128,6 +128,8 @@ "svcUnmapDeviceAddressSpace": "0x5c", "svcGetSystemInfo": "0x6f", "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", "svcCallSecureMonitor": "0x7f" } } diff --git a/emummc/source/main.c b/emummc/source/main.c index 520c1ea22..e93d3761d 100644 --- a/emummc/source/main.c +++ b/emummc/source/main.c @@ -39,8 +39,11 @@ void hook_function(uintptr_t source, uintptr_t target); void *__stack_top; uintptr_t text_base; +size_t fs_code_size; char inner_heap[INNER_HEAP_SIZE]; size_t inner_heap_size = INNER_HEAP_SIZE; +Handle self_proc_handle = 0; +u8 *fs_rw_mapping = NULL; extern char _start; extern char __argdata__; @@ -148,15 +151,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, INJECT_OFFSET(u64, 0), 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, INJECT_OFFSET(u64, 0), 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 - INJECT_OFFSET(u64, 0)))) = 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 +272,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 +411,21 @@ void __init() text_base = meminfo.addr; + // Get code size + svcQueryMemory(&meminfo, &pageinfo, INJECT_OFFSET(u64, 0)); + 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(); diff --git a/emummc/source/nx/smc.c b/emummc/source/nx/smc.c index 921d9be69..dcb9fce87 100644 --- a/emummc/source/nx/smc.c +++ b/emummc/source/nx/smc.c @@ -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; -} \ No newline at end of file +} + +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; +} diff --git a/emummc/source/nx/smc.h b/emummc/source/nx/smc.h index cb8069b28..215877f97 100644 --- a/emummc/source/nx/smc.h +++ b/emummc/source/nx/smc.h @@ -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 diff --git a/emummc/source/nx/svc.h b/emummc/source/nx/svc.h index 05d51b9ec..320666390 100644 --- a/emummc/source/nx/svc.h +++ b/emummc/source/nx/svc.h @@ -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. diff --git a/emummc/source/nx/svc.s b/emummc/source/nx/svc.s index 9a2c61524..fc03f96c3 100644 --- a/emummc/source/nx/svc.s +++ b/emummc/source/nx/svc.s @@ -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 diff --git a/emummc/source/utils/types.h b/emummc/source/utils/types.h index b39359e8f..5af01d48e 100644 --- a/emummc/source/utils/types.h +++ b/emummc/source/utils/types.h @@ -55,6 +55,10 @@ typedef volatile unsigned int 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 diff --git a/emummc/source/utils/util.h b/emummc/source/utils/util.h index bf13c8958..0c36d06ab 100644 --- a/emummc/source/utils/util.h +++ b/emummc/source/utils/util.h @@ -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 From 293c213bf299ad2453da0ae23b5c1f57681518c1 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 01:30:30 -0700 Subject: [PATCH 090/118] exo2: implement warmboot firmware --- exosphere2/warmboot/Makefile | 122 ++++++++++ .../source/warmboot_bootrom_workaround.cpp | 99 ++++++++ .../source/warmboot_bootrom_workaround.hpp | 23 ++ .../warmboot/source/warmboot_clkrst.cpp | 46 ++++ .../warmboot/source/warmboot_clkrst.hpp | 23 ++ .../warmboot/source/warmboot_cpu_cluster.cpp | 219 ++++++++++++++++++ .../warmboot/source/warmboot_cpu_cluster.hpp | 24 ++ exosphere2/warmboot/source/warmboot_dram.cpp | 130 +++++++++++ exosphere2/warmboot/source/warmboot_dram.hpp | 25 ++ .../source/warmboot_exception_vectors.s | 63 +++++ exosphere2/warmboot/source/warmboot_main.cpp | 105 +++++++++ exosphere2/warmboot/source/warmboot_main.hpp | 31 +++ exosphere2/warmboot/source/warmboot_misc.cpp | 56 +++++ exosphere2/warmboot/source/warmboot_misc.hpp | 23 ++ .../source/warmboot_secure_monitor.cpp | 130 +++++++++++ .../source/warmboot_secure_monitor.hpp | 23 ++ exosphere2/warmboot/source/warmboot_start.s | 36 +++ exosphere2/warmboot/source/warmboot_util.hpp | 23 ++ .../warmboot/source/warmboot_util_asm.s | 30 +++ exosphere2/warmboot/warmboot.ld | 189 +++++++++++++++ exosphere2/warmboot/warmboot.specs | 7 + .../libexosphere/include/exosphere/fuse.hpp | 3 + .../libexosphere/include/exosphere/pmic.hpp | 1 + .../libexosphere/include/exosphere/reg.hpp | 4 + .../include/exosphere/se/se_aes.hpp | 3 + .../libexosphere/include/exosphere/tegra.hpp | 2 + .../exosphere/tegra/tegra_apb_misc.hpp | 32 ++- .../include/exosphere/tegra/tegra_clkrst.hpp | 158 ++++++++++++- .../include/exosphere/tegra/tegra_emc.hpp | 55 +++-- .../exosphere/tegra/tegra_flow_ctlr.hpp | 5 +- .../include/exosphere/tegra/tegra_mselect.hpp | 17 ++ .../include/exosphere/tegra/tegra_pg_up.hpp | 23 ++ .../include/exosphere/tegra/tegra_pinmux.hpp | 68 ++++++ .../include/exosphere/tegra/tegra_pmc.hpp | 11 + .../include/exosphere/tegra/tegra_sb.hpp | 6 + .../include/exosphere/tegra/tegra_timer.hpp | 5 +- .../libexosphere/source/fuse/fuse_api.cpp | 58 ++++- .../source/fuse/fuse_registers.hpp | 9 +- .../libexosphere/source/pinmux/pinmux_api.cpp | 1 - .../source/pinmux/pinmux_registers.hpp | 65 ------ .../libexosphere/source/pmic/pmic_api.cpp | 20 ++ libraries/libexosphere/source/se/se_aes.cpp | 68 ++++++ .../crypto/crypto_memory_compare.arch.arm.cpp | 58 +++++ 43 files changed, 1996 insertions(+), 103 deletions(-) create mode 100644 exosphere2/warmboot/Makefile create mode 100644 exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp create mode 100644 exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp create mode 100644 exosphere2/warmboot/source/warmboot_clkrst.cpp create mode 100644 exosphere2/warmboot/source/warmboot_clkrst.hpp create mode 100644 exosphere2/warmboot/source/warmboot_cpu_cluster.cpp create mode 100644 exosphere2/warmboot/source/warmboot_cpu_cluster.hpp create mode 100644 exosphere2/warmboot/source/warmboot_dram.cpp create mode 100644 exosphere2/warmboot/source/warmboot_dram.hpp create mode 100644 exosphere2/warmboot/source/warmboot_exception_vectors.s create mode 100644 exosphere2/warmboot/source/warmboot_main.cpp create mode 100644 exosphere2/warmboot/source/warmboot_main.hpp create mode 100644 exosphere2/warmboot/source/warmboot_misc.cpp create mode 100644 exosphere2/warmboot/source/warmboot_misc.hpp create mode 100644 exosphere2/warmboot/source/warmboot_secure_monitor.cpp create mode 100644 exosphere2/warmboot/source/warmboot_secure_monitor.hpp create mode 100644 exosphere2/warmboot/source/warmboot_start.s create mode 100644 exosphere2/warmboot/source/warmboot_util.hpp create mode 100644 exosphere2/warmboot/source/warmboot_util_asm.s create mode 100644 exosphere2/warmboot/warmboot.ld create mode 100644 exosphere2/warmboot/warmboot.specs create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp create mode 100644 libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp delete mode 100644 libraries/libexosphere/source/pinmux/pinmux_registers.hpp create mode 100644 libraries/libvapours/source/crypto/crypto_memory_compare.arch.arm.cpp diff --git a/exosphere2/warmboot/Makefile b/exosphere2/warmboot/Makefile new file mode 100644 index 000000000..2288e2717 --- /dev/null +++ b/exosphere2/warmboot/Makefile @@ -0,0 +1,122 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# 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)) + +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))) + +#--------------------------------------------------------------------------------- +# 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 + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +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) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp b/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp new file mode 100644 index 000000000..f49bf2429 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp @@ -0,0 +1,99 @@ +/* + * 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 "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + + } + + void ApplyMbistWorkaround() { + /* Clear all LVL2 clock gate overrides to zero. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE, 0); + + /* Clear clock enable for all but a select few devices. */ + auto devices_to_clear_l = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_l), CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_RTC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_TMR ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_GPIO ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_CACHE2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, devices_to_clear_l); + + auto devices_to_clear_h = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_h), CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_MEM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_PMC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_FUSE), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_EMC )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, devices_to_clear_h); + + auto devices_to_clear_u = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_u), CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CSITE), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMA), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMB), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMC), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMD), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CRAM2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, devices_to_clear_u); + + auto devices_to_clear_v = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_v), CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_MSELECT ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SPDIF_DOUBLER), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_TZRAM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SE )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_CLR, devices_to_clear_v); + + auto devices_to_clear_w = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_w), CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX0), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX1), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX2), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX3), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX4), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX5), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_ENTROPY)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, devices_to_clear_w); + + auto devices_to_clear_x = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_X); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_x), CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CAPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CBPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_BBC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_EMC_DLL ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_GPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_DBGAPB ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_PLLG_REF)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_X_CLR, devices_to_clear_x); + + auto devices_to_clear_y = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_Y); + reg::ClearBits(static_cast<volatile u32 &>(devices_to_clear_y), CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CCPA), + CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CDPA)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_CLR, devices_to_clear_y); + + /* If CH1 is enabled, enable clock to MC1. */ + if (reg::HasValue(EMC + EMC_FBIO_CFG7, EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))) { + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLK_ENB_MC1, ENABLE)); + } + } + +} diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp b/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp new file mode 100644 index 000000000..039c3df20 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void ApplyMbistWorkaround(); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_clkrst.cpp b/exosphere2/warmboot/source/warmboot_clkrst.cpp new file mode 100644 index 000000000..258b3b398 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_clkrst.cpp @@ -0,0 +1,46 @@ +/* + * 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 "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t TIMER = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + } + + void ConfigureOscillators() { + /* Enable the crystal oscillator, and copy the drive strength from pmc. */ + const auto xofs = reg::GetValue(PMC + APBDEV_PMC_OSC_EDPD_OVER, PMC_REG_BITS_MASK(OSC_EDPD_OVER_XOFS)); + + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_OSC_CTRL, CLK_RST_REG_BITS_ENUM (OSC_CTRL_XOE, ENABLE), + CLK_RST_REG_BITS_VALUE(OSC_CTRL_XOFS, xofs)); + + /* Configure CLK_M_DIVISOR to 2. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0, CLK_RST_REG_BITS_ENUM(SPARE_REG0_CLK_M_DIVISOR, CLK_M_DIVISOR2)); + reg::Read(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0); + + /* Restore TIMERUS config to 19.2 MHz. */ + reg::Write(TIMER + TIMERUS_USEC_CFG, TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVIDEND, 5 - 1), + TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVISOR, 96 - 1)); + + } + +} diff --git a/exosphere2/warmboot/source/warmboot_clkrst.hpp b/exosphere2/warmboot/source/warmboot_clkrst.hpp new file mode 100644 index 000000000..327f82512 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_clkrst.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void ConfigureOscillators(); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp b/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp new file mode 100644 index 000000000..34983e02c --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp @@ -0,0 +1,219 @@ +/* + * 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 "warmboot_clkrst.hpp" +#include "warmboot_util.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t GPIO = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + constexpr inline const uintptr_t MSELECT = MSELECT(0); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + ALWAYS_INLINE void EnableClusterPartition(const reg::BitsValue value, APBDEV_PMC_PWRGATE_TOGGLE_PARTID part_id) { + /* Toggle the partitions if necessary. */ + if (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, part_id)); + } + + /* Wait for the toggle to complete. */ + while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { /* ... */ } + + /* Remove clamping. */ + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, value); + + /* Wait for clamping to be removed. */ + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, value)) { /* ... */ } + } + + } + + void InitializeCpuCluster() { + /* Set CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SET_SET_CSITE_RST, ENABLE)); + + /* Restore PROD setting to CPU_SOFTRST_CTRL2 by clearing CAR2PMC_CPU_ACK_WIDTH. */ + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2, CLK_RST_REG_BITS_VALUE(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0)); + + /* Restore the CPU reset vector from PMC scratch. */ + reg::Write(SYSTEM + SB_AA64_RESET_LOW, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH34) | 1); + reg::Write(SYSTEM + SB_AA64_RESET_HIGH, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH35)); + + /* Configure SUPER_CCLKG_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKG_DIVIDER_SUPER_CDIV_ENB, ENABLE), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); + + /* Configure SUPER_CCLKLP_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKLP_DIVIDER_SUPER_CDIV_ENB, ENABLE), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); + + /* Enable CoreSight clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_SET_SET_CLK_ENB_CSITE, ENABLE)); + + /* Clear CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_CLR_CLR_CSITE_RST, ENABLE)); + + /* Select MSELECT clock source as PLLP_OUT0 with divider of 4. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 6)); + + /* Enable clock to MSELECT. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_MSELECT, ENABLE)); + + /* Wait two microseconds, then take MSELECT out of reset. */ + util::WaitMicroSeconds(2); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_V_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_V_CLR_CLR_MSELECT_RST, ENABLE)); + + /* Workaround bug by disabling MSELECT error mechanism and enabling WRAP type conversion. */ + reg::ReadWrite(MSELECT + MSELECT_CONFIG, MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, ENABLE)); + + /* Disable PLLX. */ + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_ENABLE, DISABLE)); + + /* Clear bit 0 of PMC Scratch 190. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); + + /* Clear PMC_DPD_SAMPLE, and wait 10 us for clear to take effect. */ + reg::Write(PMC + APBDEV_PMC_DPD_SAMPLE, 0); + util::WaitMicroSeconds(10); + + /* Configure UART2_RTS low (GPIO controller 2 G). */ + reg::ReadWrite(GPIO + 0x108, REG_BITS_VALUE(2, 1, 1)); /* GPIO_CNF */ + reg::ReadWrite(GPIO + 0x118, REG_BITS_VALUE(2, 1, 1)); /* GPIO_OE */ + reg::ReadWrite(GPIO + 0x128, REG_BITS_VALUE(2, 1, 0)); /* GPIO_OUT */ + + /* Tristate CLDVFS PWN. */ + reg::Write(APB_MISC + PINMUX_AUX_DVFS_PWM, PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE), + PINMUX_REG_BITS_ENUM(AUX_DVFS_PWM_PM, CLDVFS)); + reg::Read(APB_MISC + PINMUX_AUX_DVFS_PWM); + + /* Restore PWR_I2C E_INPUT. */ + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + + /* Enable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DVFS, ENABLE)); + + /* Set CLDVFS clock source/divider. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 14)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 14)); + + /* Enable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_I2C5, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + util::WaitMicroSeconds(5); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_I2C5_I2C5_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 4)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_I2C5_RST, ENABLE)); + + /* Set the EN bit in pmic regulator. */ + pmic::SetEnBit(fuse::GetRegulator()); + + /* Wait 2ms. */ + util::WaitMicroSeconds(2'000); + + /* Enable power to the CRAIL partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL); + + /* Remove software clamp to CRAIL. */ + reg::Write(PMC + APBDEV_PMC_SET_SW_CLAMP, 0); + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, PMC_REG_BITS_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, ENABLE)); + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } + + /* Spinloop 8 times, to add a little delay. */ + SpinLoop(8); + + /* Disable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_I2C5, ENABLE)); + + /* Disable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLR_CLR_CLK_ENB_DVFS, ENABLE)); + + /* Perform fast cluster RAM repair if needed. */ + if (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, SLOW))) { + reg::Write(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_REQ, ENABLE)); + + while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_STS, DONE))) { + /* ... */ + } + } + + /* Enable power to the non-cpu partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_C0NC, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC); + + /* Enable clock to PLLP_OUT_CPU. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_SET_SET_CLK_ENB_PLLP_OUT_CPU, ENABLE)); + util::WaitMicroSeconds(2); + + /* Enable clock to the cpu complex. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_CPU, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_CPUG, ENABLE)); + util::WaitMicroSeconds(10); + + /* Select cpu complex clock source. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKG_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + util::WaitMicroSeconds(10); + + /* Wake non-cpu out of reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, ENABLE)); + } + + void PowerOnCpu() { + /* Enable power to the CE0 partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CE0, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0); + + /* Clear CPU reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, ENABLE)); + } + +} diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp b/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp new file mode 100644 index 000000000..6b2e3c8be --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp @@ -0,0 +1,24 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void InitializeCpuCluster(); + void PowerOnCpu(); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_dram.cpp b/exosphere2/warmboot/source/warmboot_dram.cpp new file mode 100644 index 000000000..10999644d --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_dram.cpp @@ -0,0 +1,130 @@ +/* + * 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 "warmboot_dram.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + constexpr inline const uintptr_t EMC0 = EMC0_ADDRESS(0); + constexpr inline const uintptr_t EMC1 = EMC1_ADDRESS(0); + constexpr inline const uintptr_t MC = secmon::MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + constexpr inline const uintptr_t MC0 = secmon::MemoryRegionPhysicalDeviceMemoryController0.GetAddress(); + constexpr inline const uintptr_t MC1 = secmon::MemoryRegionPhysicalDeviceMemoryController1.GetAddress(); + + } + + void RestrictBpmpAccessToMainMemory() { + /* Bpmp memory access is restricted by forcing internal access to an invalid carveout. */ + constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); + constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + + constexpr u32 CarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + /* Specify a 128KB carveout at NULL with no clients allowed access, and bpmp forced to access. */ + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, 1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, ForceInternalAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, ForceInternalAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CFG0, CarveoutConfig); + } + + void RestoreRamSvop() { + reg::ReadWrite(APB_MISC + APB_MISC_GP_ASDBGREG, APB_MISC_REG_BITS_VALUE(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 2)); + } + + void ConfigureEmcPmacroTraining() { + /* Disable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + + /* Set E_WRPTR on Channel 0. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_0, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, DISABLED)); + + /* Set E_WRPTR on Channel 1. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_1, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, DISABLED)); + + /* Re-enable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + } + +} diff --git a/exosphere2/warmboot/source/warmboot_dram.hpp b/exosphere2/warmboot/source/warmboot_dram.hpp new file mode 100644 index 000000000..0e8cbaa88 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_dram.hpp @@ -0,0 +1,25 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void RestrictBpmpAccessToMainMemory(); + void RestoreRamSvop(); + void ConfigureEmcPmacroTraining(); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_exception_vectors.s b/exosphere2/warmboot/source/warmboot_exception_vectors.s new file mode 100644 index 000000000..7bac9ac54 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_exception_vectors.s @@ -0,0 +1,63 @@ +/* + * 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/>. + */ + +.section .vectors, "ax", %progbits +.align 3 +.global warmboot_header +warmboot_header: + /* TODO: If Mariko warmboothax ever happens, generate a mariko header? */ + /* Warmboot header. */ + .word __total_size__ + .rept 3 + .word 0x00000000 + .endr + + /* RSA modulus. */ + .rept 0x40 + .word 0xFFFFFFFF + .endr + + /* Padding */ + .rept 4 + .word 0x00000000 + .endr + + /* RSA signature */ + .rept 0x40 + .word 0xFFFFFFFF + .endr + + /* Padding */ + .rept 4 + .word 0x00000000 + .endr + + /* Firmware metadata. */ + .word __total_size__ + .word _reset + .word _reset + .word __executable_size__ + +.global _reset +_reset: + b _ZN3ams8warmboot5StartEv + +.global _metadata +_metadata: + .ascii "WBT0" /* Magic number */ + .word 0x00000000 /* Target firmware. */ + .word 0x00000000 /* Reserved */ + .word 0x00000000 /* Reserved */ \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_main.cpp b/exosphere2/warmboot/source/warmboot_main.cpp new file mode 100644 index 000000000..933b17e37 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_main.cpp @@ -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/>. + */ +#include <exosphere.hpp> +#include "warmboot_bootrom_workaround.hpp" +#include "warmboot_clkrst.hpp" +#include "warmboot_cpu_cluster.hpp" +#include "warmboot_dram.hpp" +#include "warmboot_main.hpp" +#include "warmboot_misc.hpp" +#include "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + + constexpr inline const uintptr_t ExpectedMetadataAddress = 0x40010244; + + } + + void Main(const Metadata *metadata) { + /* Ensure that we're running under vaguely sane conditions. */ + AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic); + AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max); + + /* Restrict the bpmp's access to dram. */ + if (metadata->target_firmware >= TargetFirmware_4_0_0) { + RestrictBpmpAccessToMainMemory(); + } + + /* Configure rtck-daisychaining/jtag. */ + ConfigureMiscSystemDebug(); + + /* NOTE: Here, Nintendo checks that the number of burnt anti-downgrade fuses is valid. */ + + /* NOTE: Here, Nintendo validates that APBDEV_PMC_SECURE_SCRATCH32 contains the correct magic number for the current warmboot firmware revision. */ + + /* Validate that we're executing at the correct address. */ + AMS_ABORT_UNLESS(reinterpret_cast<uintptr_t>(metadata) == ExpectedMetadataAddress); + + /* Validate that we're executing on the bpmp. */ + AMS_ABORT_UNLESS(reg::Read(PG_UP(PG_UP_TAG)) == PG_UP_TAG_PID_COP); + + /* Configure fuse bypass. */ + fuse::ConfigureFuseBypass(); + + /* Configure system oscillators. */ + ConfigureOscillators(); + + /* Restore DRAM configuration. */ + RestoreRamSvop(); + ConfigureEmcPmacroTraining(); + + /* If on Erista, work around the bootrom mbist issue. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + ApplyMbistWorkaround(); + } + + /* Initialize the cpu cluster. */ + InitializeCpuCluster(); + + /* Restore the secure monitor to tzram. */ + RestoreSecureMonitorToTzram(metadata->target_firmware); + + /* Power on the cpu. */ + PowerOnCpu(); + + /* Halt ourselves. */ + const bool disable_jtag = metadata->target_firmware >= TargetFirmware_4_0_0; + while (true) { + reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, disable_jtag, DISABLED, ENABLED)); + } + } + + NORETURN void ExceptionHandler() { + /* Write enable to MAIN_RESET. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); + while (true) { /* ... */ } + } + +} + +namespace ams::diag { + + void AbortImpl() { + warmboot::ExceptionHandler(); + } + +} diff --git a/exosphere2/warmboot/source/warmboot_main.hpp b/exosphere2/warmboot/source/warmboot_main.hpp new file mode 100644 index 000000000..f71a89b1a --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_main.hpp @@ -0,0 +1,31 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + struct Metadata { + static constexpr u32 Magic = util::FourCC<'W','B','T','0'>::Code; + + u32 magic; + ams::TargetFirmware target_firmware; + u32 reserved[2]; + }; + static_assert(util::is_pod<Metadata>::value); + static_assert(sizeof(Metadata) == 0x10); + +} diff --git a/exosphere2/warmboot/source/warmboot_misc.cpp b/exosphere2/warmboot/source/warmboot_misc.cpp new file mode 100644 index 000000000..eeb5932f2 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_misc.cpp @@ -0,0 +1,56 @@ +/* + * 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 "warmboot_misc.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + } + + void ConfigureMiscSystemDebug() { + /* Enable RTCK daisy-chaining. */ + reg::Write(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM(PP_CONFIG_CTL_TBE, ENABLE)); + + /* If we're in production mode, perform JTAG configuration. */ + /* NOTE: While this is what NVidia's code does, this is almost certainly a logic error. */ + /* They intend to configure JTAG only when *not* in production mode. */ + /* However, here we will do what they do, and not what they intend. */ + const bool production_mode = fuse::IsOdmProductionMode(); + if (production_mode) { + const bool jtag_sts = reg::HasValue(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM(STICKY_BITS_JTAG_STS, ENABLE)); + + reg::ReadWrite(SYSTEM + SB_PFCFG, SB_REG_BITS_ENUM_SEL(PFCFG_DBGEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM_SEL(PFCFG_NIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM_SEL(PFCFG_SPNIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM (PFCFG_SPIDEN, DISABLE)); + + reg::ReadWrite(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM_SEL(PP_CONFIG_CTL_JTAG, jtag_sts, ENABLE, DISABLE)); + } + + /* Configure HDA codec disable. */ + reg::ReadWrite(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM_SEL(STICKY_BITS_HDA_LPBK_DIS, production_mode, ENABLE, DISABLE)); + + /* Set E_INPUT in PINMUX_AUX_GPIO_PA6 (needed by the XUSB and SATA controllers). */ + reg::ReadWrite(APB_MISC + PINMUX_AUX_GPIO_PA6, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + } + +} diff --git a/exosphere2/warmboot/source/warmboot_misc.hpp b/exosphere2/warmboot/source/warmboot_misc.hpp new file mode 100644 index 000000000..4cfeeab20 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_misc.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void ConfigureMiscSystemDebug(); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.cpp b/exosphere2/warmboot/source/warmboot_secure_monitor.cpp new file mode 100644 index 000000000..40b7b84c3 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_secure_monitor.cpp @@ -0,0 +1,130 @@ +/* + * 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 "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline const pkg1::AesKeySlot SavedAesKeySlots[] = { + pkg1::AesKeySlot_TzramSaveKek, + pkg1::AesKeySlot_RandomForUserWrap, + pkg1::AesKeySlot_RandomForKeyStorageWrap, + pkg1::AesKeySlot_DeviceMaster, + pkg1::AesKeySlot_Master, + pkg1::AesKeySlot_Device, + }; + + constexpr ALWAYS_INLINE bool IsSavedAesKeySlot(int slot) { + for (const auto SavedSlot : SavedAesKeySlots) { + if (slot == SavedSlot) { + return true; + } + } + + return false; + } + + void ClearUnsavedSecurityEngineKeySlots() { + /* Clear unsaved aes keys and all ivs. */ + for (int slot = 0; slot < se::AesKeySlotCount; ++slot) { + if (!IsSavedAesKeySlot(slot)) { + se::ClearAesKeySlot(slot); + } + se::ClearAesKeyIv(slot); + } + + /* Clear all rsa keys. */ + for (int slot = 0; slot < se::RsaKeySlotCount; ++slot) { + se::ClearRsaKeySlot(slot); + } + } + + void RestoreEncryptedTzram(void * const tzram_dst, const void * const tzram_src, size_t tzram_size) { + /* Derive the save key from the save kek. */ + const u32 key_source[se::AesBlockSize / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27)}; + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + + /* Decrypt tzram. */ + const u8 tzram_iv[se::AesBlockSize] = {}; + se::DecryptAes256Cbc(tzram_dst, tzram_size, pkg1::AesKeySlot_TzramSaveKey, tzram_src, tzram_size, tzram_iv, sizeof(tzram_iv)); + + /* Calculate the cmac of decrypted tzram. */ + u8 tzram_mac[se::AesBlockSize] = {}; + se::ComputeAes256Cmac(tzram_mac, sizeof(tzram_mac), pkg1::AesKeySlot_TzramSaveKey, tzram_dst, tzram_size); + + /* Get the expected mac from pmc scratch. */ + const u32 expected_mac[sizeof(tzram_mac) / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115)}; + + /* Validate that the calculated mac is correct. */ + AMS_ABORT_UNLESS(crypto::IsSameBytes(tzram_mac, expected_mac, sizeof(tzram_mac))); + } + + void RestoreSecureMonitorToTzramErista(const TargetFirmware target_fw) { + /* Clear all unsaved security engine keyslots. */ + ClearUnsavedSecurityEngineKeySlots(); + + /* Restore encrypted tzram contents. */ + void * const tzram_src = secmon::MemoryRegionPhysicalDramSecureDataStoreTzram.GetPointer<void>(); + void * const tzram_dst = secmon::MemoryRegionPhysicalTzramNonVolatile.GetPointer<void>(); + const size_t tzram_size = secmon::MemoryRegionPhysicalTzramNonVolatile.GetSize(); + RestoreEncryptedTzram(tzram_dst, tzram_src, tzram_size); + + /* Clear the tzram kek registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, 0); + + /* Clear the tzram cmac registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, 0); + + /* Clear the keydata used to protect tzram. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKek); + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + + /* Clear the encrypted copy of tzram in dram. */ + /* NOTE: This does not actually clear the encrypted copy, because BPMP access to main memory has been restricted. */ + /* Nintendo seems to not realize this, though, so we'll do the same. */ + std::memset(tzram_src, 0, tzram_size); + + /* Set Tzram to secure-world only. */ + se::SetTzramSecure(); + } + + } + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw) { + /* If erista, perform restoration procedure. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + RestoreSecureMonitorToTzramErista(target_fw); + } + + /* Lock secure scratch. */ + pmc::LockSecureRegister(static_cast<pmc::SecureRegister>(pmc::SecureRegister_DramParameters | pmc::SecureRegister_Other)); + + /* Lockout fuses. */ + fuse::Lockout(); + } + +} diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.hpp b/exosphere2/warmboot/source/warmboot_secure_monitor.hpp new file mode 100644 index 000000000..499d125c6 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_secure_monitor.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_start.s b/exosphere2/warmboot/source/warmboot_start.s new file mode 100644 index 000000000..18070e04a --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_start.s @@ -0,0 +1,36 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams8warmboot5StartEv, "ax", %progbits +.align 3 +.global _ZN3ams8warmboot5StartEv +_ZN3ams8warmboot5StartEv: + /* Set CPSR_cf and CPSR_cf. */ + msr cpsr_f, #0xC0 + msr cpsr_cf, #0xD3 + + /* Set the stack pointer. */ + ldr sp, =__stack_top__ + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams8warmboot16ExceptionHandlerEv + + /* Invoke main. */ + ldr r0, =_metadata + bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_util.hpp b/exosphere2/warmboot/source/warmboot_util.hpp new file mode 100644 index 000000000..5ca4402e6 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_util.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <exosphere.hpp> + +namespace ams::warmboot { + + void SpinLoop(unsigned int num); + +} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_util_asm.s b/exosphere2/warmboot/source/warmboot_util_asm.s new file mode 100644 index 000000000..c23d7f307 --- /dev/null +++ b/exosphere2/warmboot/source/warmboot_util_asm.s @@ -0,0 +1,30 @@ +/* + * 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/>. + */ + +.section .text._ZN3ams8warmboot8SpinLoopEj, "ax", %progbits +.global _ZN3ams8warmboot8SpinLoopEj +.thumb_func +.syntax unified +_ZN3ams8warmboot8SpinLoopEj: + 1: + /* Subtract one from the count. */ + subs r0, r0, #1 + + /* If we aren't at zero, continue looping. */ + bgt 1b + + /* Return. */ + bx lr \ No newline at end of file diff --git a/exosphere2/warmboot/warmboot.ld b/exosphere2/warmboot/warmboot.ld new file mode 100644 index 000000000..0e3d4482f --- /dev/null +++ b/exosphere2/warmboot/warmboot.ld @@ -0,0 +1,189 @@ +OUTPUT_ARCH(arm) +ENTRY(_reset) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + lp0fw : ORIGIN = 0x40010000, LENGTH = 16K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(lp0fw)); + . = __start__; + __code_start = . ; + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >lp0fw + + .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); + } >lp0fw + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >lp0fw + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >lp0fw + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >lp0fw + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >lp0fw + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >lp0fw + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >lp0fw + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >lp0fw + + .hash : { *(.hash) } >lp0fw + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >lp0fw + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >lp0fw + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >lp0fw + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >lp0fw + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >lp0fw + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >lp0fw + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >lp0fw + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >lp0fw + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >lp0fw + .got.plt : { *(.got.plt) *(.igot.plt) } >lp0fw + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >lp0fw + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >lp0fw + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + __total_size__ = (__end__ - __start__); + __executable_size__ = (__end__ - _reset); + + __stack_top__ = 0x40013000; + __stack_bottom__ = 0x40012000; + + /* ================== + ==== 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) } +} \ No newline at end of file diff --git a/exosphere2/warmboot/warmboot.specs b/exosphere2/warmboot/warmboot.specs new file mode 100644 index 000000000..45d00560d --- /dev/null +++ b/exosphere2/warmboot/warmboot.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index eef79fed7..19b58dca7 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -109,4 +109,7 @@ namespace ams::fuse { int GetExpectedFuseVersion(TargetFirmware target_fw); bool HasRcmVulnerabilityPatch(); + bool IsOdmProductionMode(); + void ConfigureFuseBypass(); + } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp index 29122bce2..fc43715b8 100644 --- a/libraries/libexosphere/include/exosphere/pmic.hpp +++ b/libraries/libexosphere/include/exosphere/pmic.hpp @@ -27,6 +27,7 @@ namespace ams::pmic { Regulator_Mariko_Max77812_B = 2, /* Device code 0x3A000006 */ }; + void SetEnBit(Regulator regulator); void EnableVddCpu(Regulator regulator); void DisableVddCpu(Regulator regulator); void EnableSleep(); diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp index aa0de0831..1ac314110 100644 --- a/libraries/libexosphere/include/exosphere/reg.hpp +++ b/libraries/libexosphere/include/exosphere/reg.hpp @@ -87,6 +87,10 @@ namespace ams::reg { template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...)) ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + ALWAYS_INLINE u32 GetValue(volatile u32 *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } + ALWAYS_INLINE u32 GetValue(volatile u32 ®, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } + ALWAYS_INLINE u32 GetValue(uintptr_t reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } + ALWAYS_INLINE void ReadWrite(volatile u32 *reg, u32 val, u32 mask) { *reg = (*reg & (~mask)) | (val & mask); } ALWAYS_INLINE void ReadWrite(volatile u32 ®, u32 val, u32 mask) { reg = ( reg & (~mask)) | (val & mask); } ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask); } diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index bd8c46ced..d3f18d20c 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -23,6 +23,7 @@ namespace ams::se { constexpr inline size_t AesBlockSize = crypto::AesEncryptor128::BlockSize; void ClearAesKeySlot(int slot); + void ClearAesKeyIv(int slot); void LockAesKeySlot(int slot, u32 flags); void SetAesKey(int slot, const void *key, size_t key_size); @@ -40,6 +41,8 @@ namespace ams::se { void EncryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); void EncryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void DecryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void DecryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); diff --git a/libraries/libexosphere/include/exosphere/tegra.hpp b/libraries/libexosphere/include/exosphere/tegra.hpp index a6c8a0dda..7f988871f 100644 --- a/libraries/libexosphere/include/exosphere/tegra.hpp +++ b/libraries/libexosphere/include/exosphere/tegra.hpp @@ -26,6 +26,8 @@ #include <exosphere/tegra/tegra_ictlr.hpp> #include <exosphere/tegra/tegra_mc.hpp> #include <exosphere/tegra/tegra_mselect.hpp> +#include <exosphere/tegra/tegra_pinmux.hpp> +#include <exosphere/tegra/tegra_pg_up.hpp> #include <exosphere/tegra/tegra_pmc.hpp> #include <exosphere/tegra/tegra_sb.hpp> #include <exosphere/tegra/tegra_sysctr0.hpp> diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp index af05f3658..4ad9de8ba 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp @@ -16,22 +16,32 @@ #pragma once #include <vapours.hpp> +#define APB_MISC_PP_CONFIG_CTL (0x024) + +#define APB_MISC_GP_ASDBGREG (0x810) + +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) #define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) #define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 (0xc04) #define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 (0xc08) -#define AHB_MISC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AHB_MISC, NAME) -#define AHB_MISC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AHB_MISC, NAME, VALUE) -#define AHB_MISC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AHB_MISC, NAME, ENUM) -#define AHB_MISC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AHB_MISC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) +#define APB_MISC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APB_MISC, NAME) +#define APB_MISC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (APB_MISC, NAME, VALUE) +#define APB_MISC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (APB_MISC, NAME, ENUM) +#define APB_MISC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(APB_MISC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) -#define DEFINE_AHB_MISC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AHB_MISC, NAME, __OFFSET__, __WIDTH__) -#define DEFINE_AHB_MISC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE) -#define DEFINE_AHB_MISC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) -#define DEFINE_AHB_MISC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) -#define DEFINE_AHB_MISC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) +#define DEFINE_APB_MISC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (APB_MISC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_APB_MISC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_APB_MISC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_APB_MISC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_APB_MISC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (APB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) -#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_AHB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE) +DEFINE_APB_MISC_REG_BIT_ENUM(PP_CONFIG_CTL_JTAG, 6, DISABLE, ENABLE); +DEFINE_APB_MISC_REG_BIT_ENUM(PP_CONFIG_CTL_TBE, 7, DISABLE, ENABLE); + +DEFINE_APB_MISC_REG(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 24, 2); + +#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_APB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE) DEFINE_SLAVE_SECURITY_REG(0, 29, STM); DEFINE_SLAVE_SECURITY_REG(0, 24, CEC); @@ -96,4 +106,4 @@ DEFINE_SLAVE_SECURITY_REG(2, 0, SDMMC1); #undef DEFINE_SLAVE_SECURITY_REG -#define SLAVE_SECURITY_REG_BITS_ENUM(RINDEX, NAME, ENUM) AHB_MISC_REG_BITS_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, ENUM) +#define SLAVE_SECURITY_REG_BITS_ENUM(RINDEX, NAME, ENUM) APB_MISC_REG_BITS_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, ENUM) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index fc2253c27..649ed3f04 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -29,14 +29,51 @@ #define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) -#define CLK_RST_CONTROLLER_RST_SOURCE (0x000) +#define CLK_RST_CONTROLLER_RST_SOURCE (0x000) -#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) +#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) +#define CLK_RST_CONTROLLER_OSC_CTRL (0x050) +#define CLK_RST_CONTROLLER_PLLX_BASE (0x0E0) +#define CLK_RST_CONTROLLER_CCLKG_BURST_POLICY (0x368) +#define CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER (0x36C) +#define CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY (0x370) +#define CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER (0x374) +#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 (0x388) +#define CLK_RST_CONTROLLER_SPARE_REG0 (0x55C) +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA (0x0F8) +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB (0x0FC) +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC (0x3A0) #define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD (0x3A4) +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE (0x554) DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); +DEFINE_CLK_RST_REG_BIT_ENUM(OSC_CTRL_XOE, 0, DISABLE, ENABLE); +DEFINE_CLK_RST_REG(OSC_CTRL_XOFS, 4, 6); + +DEFINE_CLK_RST_REG_BIT_ENUM(PLLX_BASE_PLLX_ENABLE, 30, DISABLE, ENABLE); + +DEFINE_CLK_RST_REG(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 8, 8); +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, 24, NO_IMPACT, DISABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, 25, NO_IMPACT, DISABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, 26, NO_IMPACT, DISABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, 27, NO_IMPACT, DISABLE); + +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLKG_DIVIDER_SUPER_CDIV_ENB, 31, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(SUPER_CCLKLP_DIVIDER_SUPER_CDIV_ENB, 31, DISABLE, ENABLE); + +DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, 0, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ); +DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, 4, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ); +DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, 8, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ); +DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, 12, CLKM, RSVD1, CLKS, RSVD3, PLLP_OUT0, PLLP_OUT4, RSVD6, RSVD7, PLLX_OUT0_LJ, DVFS_CPU_CLK, RSVD10, RSVD11, RSVD12, RSVD13, PLLX_OUT0, DVFS_CPU_CLK_LJ); +DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(CCLK_BURST_POLICY_CPU_STATE, 28, STDBY, IDLE, RUN, RSVD3, IRQ, RSVD5, RSVD6, RSVD7, FIQ, RSVD9, RSVD10, RSVD11, RSVD12, RSVD13, RSVD14, RSVD15); + +DEFINE_CLK_RST_REG(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0, 12); + +DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK_M_DIVISOR2, CLK_M_DIVISOR3, CLK_M_DIVISOR4); + /* RST_DEVICES */ #define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004) #define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008) @@ -56,18 +93,45 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) /* CLK_SOURCE */ -#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124) -#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128) -#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) -#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) -#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) -#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8) +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124) +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) +#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4) +#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8) +#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C) +#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630) /* RST_DEV_*_SET */ #define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300) +#define CLK_RST_CONTROLLER_RST_DEV_H_SET (0x308) +#define CLK_RST_CONTROLLER_RST_DEV_U_SET (0x310) +#define CLK_RST_CONTROLLER_RST_DEV_V_SET (0x430) /* RST_DEV_*_CLR */ #define CLK_RST_CONTROLLER_RST_DEV_L_CLR (0x304) +#define CLK_RST_CONTROLLER_RST_DEV_H_CLR (0x30C) +#define CLK_RST_CONTROLLER_RST_DEV_U_CLR (0x314) +#define CLK_RST_CONTROLLER_RST_DEV_V_CLR (0x434) + +/* CLK_ENB_*_SET */ +#define CLK_RST_CONTROLLER_CLK_ENB_L_SET (0x320) +#define CLK_RST_CONTROLLER_CLK_ENB_H_SET (0x328) +#define CLK_RST_CONTROLLER_CLK_ENB_U_SET (0x330) +#define CLK_RST_CONTROLLER_CLK_ENB_V_SET (0x440) +#define CLK_RST_CONTROLLER_CLK_ENB_W_SET (0x448) +#define CLK_RST_CONTROLLER_CLK_ENB_X_SET (0x284) +#define CLK_RST_CONTROLLER_CLK_ENB_Y_SET (0x29C) + +/* CLK_ENB_*_CLR */ +#define CLK_RST_CONTROLLER_CLK_ENB_L_CLR (0x324) +#define CLK_RST_CONTROLLER_CLK_ENB_H_CLR (0x32C) +#define CLK_RST_CONTROLLER_CLK_ENB_U_CLR (0x334) +#define CLK_RST_CONTROLLER_CLK_ENB_X_CLR (0x288) +#define CLK_RST_CONTROLLER_CLK_ENB_Y_CLR (0x2A0) +#define CLK_RST_CONTROLLER_CLK_ENB_V_CLR (0x444) +#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR (0x44C) /* CLK_ENB_*_INDEX */ #define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C) @@ -95,14 +159,92 @@ DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, O DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG(CLK_SOURCE_I2C1_I2C1_CLK_DIVISOR, 0, 8); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 0, 8); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT2, PLLC4_OUT1, CLK_S, CLK_M, PLLC4_OUT0); +DEFINE_CLK_RST_REG(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 0, 8); + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); + +DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE); DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE); + +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, 0, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET1, 1, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET2, 2, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET3, 3, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, 16, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET1, 17, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET2, 18, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET3, 19, DISABLE, ENABLE); +DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENABLE); + +/* TODO: Actually include all devices. */ +#define CLK_RST_FOREACH_DEVICE(HANDLER) \ + HANDLER(L, CPU, 0, 0) \ + HANDLER(L, RTC, 0, 4) \ + HANDLER(L, TMR, 0, 5) \ + HANDLER(L, GPIO, 0, 8) \ + HANDLER(L, CACHE2, 0, 31) \ + HANDLER(H, MEM, 1, 0) \ + HANDLER(H, PMC, 1, 6) \ + HANDLER(H, FUSE, 1, 7) \ + HANDLER(H, I2C5, 1, 15) \ + HANDLER(H, EMC, 1, 25) \ + HANDLER(U, CSITE, 2, 9) \ + HANDLER(U, IRAMA, 2, 20) \ + HANDLER(U, IRAMB, 2, 21) \ + HANDLER(U, IRAMC, 2, 22) \ + HANDLER(U, IRAMD, 2, 23) \ + HANDLER(U, CRAM2, 2, 24) \ + HANDLER(V, CPUG, 3, 0) \ + HANDLER(V, MSELECT, 3, 3) \ + HANDLER(V, SPDIF_DOUBLER, 3, 22) \ + HANDLER(V, TZRAM, 3, 30) \ + HANDLER(V, SE, 3, 31) \ + HANDLER(W, PCIERX0, 4, 2) \ + HANDLER(W, PCIERX1, 4, 3) \ + HANDLER(W, PCIERX2, 4, 4) \ + HANDLER(W, PCIERX3, 4, 5) \ + HANDLER(W, PCIERX4, 4, 6) \ + HANDLER(W, PCIERX5, 4, 7) \ + HANDLER(W, ENTROPY, 4, 21) \ + HANDLER(W, DVFS, 4, 27) \ + HANDLER(W, MC1, 4, 30) \ + HANDLER(X, MC_CAPA, 5, 7) \ + HANDLER(X, MC_CBPA, 5, 8) \ + HANDLER(X, MC_CPU, 5, 9) \ + HANDLER(X, MC_BBC, 5, 10) \ + HANDLER(X, EMC_DLL, 5, 14) \ + HANDLER(X, GPU, 5, 24) \ + HANDLER(X, DBGAPB, 5, 25) \ + HANDLER(X, PLLG_REF, 5, 29) \ + HANDLER(Y, MC_CCPA, 6, 8) \ + HANDLER(Y, MC_CDPA, 6, 9) \ + HANDLER(Y, PLLP_OUT_CPU, 6, 31) + +#define CLK_RST_DEFINE_SET_CLR_REG(REGISTER, DEVICE, REGISTER_INDEX, DEVICE_INDEX) \ + DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_SET_SET_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLR_CLR_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_SET_SET_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_CLR_CLR_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); + +CLK_RST_FOREACH_DEVICE(CLK_RST_DEFINE_SET_CLR_REG) + +#undef CLK_RST_DEFINE_SET_CLR_REG + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp index f072b0196..7078ea199 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp @@ -20,20 +20,23 @@ #define EMC0_ADDRESS(x) (0x7001E000 + x) #define EMC1_ADDRESS(x) (0x7001F000 + x) -#define EMC_CFG (0x00C) -#define EMC_ADR_CFG (0x010) -#define EMC_TIMING_CONTROL (0x028) -#define EMC_SELF_REF (0x0E0) -#define EMC_MRW (0x0E8) -#define EMC_FBIO_CFG5 (0x104) -#define EMC_MRW3 (0x138) -#define EMC_AUTO_CAL_CONFIG (0x2A4) -#define EMC_REQ_CTRL (0x2B0) -#define EMC_EMC_STATUS (0x2B4) -#define EMC_CFG_DIG_DLL (0x2BC) -#define EMC_ZCAL_INTERVAL (0x2E0) -#define EMC_PMC_SCRATCH3 (0x448) -#define EMC_FBIO_CFG7 (0x584) +#define EMC_CFG (0x00C) +#define EMC_ADR_CFG (0x010) +#define EMC_TIMING_CONTROL (0x028) +#define EMC_SELF_REF (0x0E0) +#define EMC_MRW (0x0E8) +#define EMC_FBIO_CFG5 (0x104) +#define EMC_MRW3 (0x138) +#define EMC_AUTO_CAL_CONFIG (0x2A4) +#define EMC_REQ_CTRL (0x2B0) +#define EMC_EMC_STATUS (0x2B4) +#define EMC_CFG_DIG_DLL (0x2BC) +#define EMC_ZCAL_INTERVAL (0x2E0) +#define EMC_PMC_SCRATCH3 (0x448) +#define EMC_FBIO_CFG7 (0x584) +#define EMC_PMACRO_CFG_PM_GLOBAL_0 (0xC30) +#define EMC_PMACRO_TRAINING_CTRL_0 (0xCF8) +#define EMC_PMACRO_TRAINING_CTRL_1 (0xCFC) #define EMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (EMC, NAME) #define EMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (EMC, NAME, VALUE) @@ -88,3 +91,27 @@ DEFINE_EMC_REG_BIT_ENUM(PMC_SCRATCH3_WEAK_BIAS, 30, DISABLED, ENABLED); DEFINE_EMC_REG_BIT_ENUM(FBIO_CFG7_CH1_ENABLE, 2, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, 16, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, 17, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, 18, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, 19, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, 20, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, 21, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, 22, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, 23, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, 24, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, 25, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, 26, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, 27, DISABLE, ENABLE); + +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, 0, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, 1, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, 2, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, 3, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, 4, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, 0, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, 1, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, 2, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, 3, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, 4, DISABLED, ENABLED); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index eebdcc400..ef74c7f78 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -16,7 +16,7 @@ #pragma once #include <vapours.hpp> - +#define FLOW_CTLR_RAM_REPAIR (0x040) #define FLOW_CTLR_FLOW_DBG_QUAL (0x050) #define FLOW_CTLR_L2FLUSH_CONTROL (0x094) #define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098) @@ -66,6 +66,9 @@ DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MO DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(RAM_REPAIR_REQ, 0, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(RAM_REPAIR_STS, 1, REQUESTED, DONE); + DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, 0, FAST, SLOW); DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, 1, DISABLE, ENABLE); DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, 2, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp index 20c5530df..6f0c5137b 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp @@ -20,3 +20,20 @@ #define MSELECT_CONFIG (0x000) +#define MSELECT_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MSELECT, NAME) +#define MSELECT_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MSELECT, NAME, VALUE) +#define MSELECT_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MSELECT, NAME, ENUM) +#define MSELECT_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MSELECT, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_MSELECT_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MSELECT, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_MSELECT_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_MSELECT_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_MSELECT_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_MSELECT_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MSELECT, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, 24, DISABLE, ENABLE); +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, 25, DISABLE, ENABLE); +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, 27, DISABLE, ENABLE); +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, 28, DISABLE, ENABLE); +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, 29, DISABLE, ENABLE); +DEFINE_MSELECT_REG_BIT_ENUM(CONFIG_WRAP_TO_INCR_SLAVE3, 30, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp new file mode 100644 index 000000000..a461d28d2 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp @@ -0,0 +1,23 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define PG_UP(x) (0x60000000 + x) + +#define PG_UP_TAG (0x000) + +#define PG_UP_TAG_PID_COP 0xAAAAAAAA diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp new file mode 100644 index 000000000..8e5424880 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp @@ -0,0 +1,68 @@ +/* + * 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/>. + */ +#pragma once +#include <vapours.hpp> + +#define PINMUX_AUX_GEN1_I2C_SCL (0x30BC) +#define PINMUX_AUX_GEN1_I2C_SDA (0x30C0) +#define PINMUX_AUX_PWR_I2C_SCL (0x30DC) +#define PINMUX_AUX_PWR_I2C_SDA (0x30E0) + +#define PINMUX_AUX_UART1_TX (0x30E4) +#define PINMUX_AUX_UART1_RX (0x30E8) +#define PINMUX_AUX_UART1_RTS (0x30EC) +#define PINMUX_AUX_UART1_CTS (0x30F0) +#define PINMUX_AUX_UART2_TX (0x30F4) +#define PINMUX_AUX_UART2_RX (0x30F8) +#define PINMUX_AUX_UART2_RTS (0x30FC) +#define PINMUX_AUX_UART2_CTS (0x3100) +#define PINMUX_AUX_UART3_TX (0x3104) +#define PINMUX_AUX_UART3_RX (0x3108) +#define PINMUX_AUX_UART3_RTS (0x310C) +#define PINMUX_AUX_UART3_CTS (0x3110) +#define PINMUX_AUX_DVFS_PWM (0x3184) +#define PINMUX_AUX_GPIO_PA6 (0x3244) + +#define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME) +#define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE) +#define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM) +#define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE); +DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE); + +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3); +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3); + +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3); +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART); +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3); + +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_DVFS_PWM_PM, 0, RSVD0, CLDVFS, SPI3, RSVD3); + +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GPIO_PA6_PM, 0, SATA, RSVD1, RSVD2, RSVD3); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index da1c6d053..6934c8ccb 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -50,6 +50,7 @@ #define APBDEV_PMC_WAKE2_LVL (0x164) #define APBDEV_PMC_WAKE2_STATUS (0x168) #define APBDEV_PMC_AUTO_WAKE2_LVL_MASK (0x170) +#define APBDEV_PMC_OSC_EDPD_OVER (0x1A4) #define APBDEV_PMC_CLK_OUT_CNTRL (0x1A8) #define APBDEV_PMC_IO_DPD_REQ (0x1B8) #define APBDEV_PMC_IO_DPD_STATUS (0x1BC) @@ -59,6 +60,7 @@ #define APBDEV_PMC_SCRATCH45 (0x234) #define APBDEV_PMC_SCRATCH46 (0x238) #define APBDEV_PMC_TSC_MULT (0x2B4) +#define APBDEV_PMC_STICKY_BITS (0x2C0) #define APBDEV_PMC_WEAK_BIAS (0x2C8) #define APBDEV_PMC_GPU_RG_CNTRL (0x2D4) #define APBDEV_PMC_CNTRL2 (0x440) @@ -162,6 +164,8 @@ enum APBDEV_PMC_PWRGATE_TOGGLE_PARTID : u8 { APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE2 = 29, }; +DEFINE_PMC_REG_BIT_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, 0, DISABLE, ENABLE); + enum APBDEV_PMC_PWRGATE_STATUS_STATUS { APBDEV_PMC_PWRGATE_STATUS_STATUS_OFF = 0, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON = 1, @@ -221,4 +225,11 @@ DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBC, 22, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VIC, 23, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_IRAM, 24, DISABLE, ENABLE); +DEFINE_PMC_REG(OSC_EDPD_OVER_XOFS, 1, 6); + +DEFINE_PMC_REG_BIT_ENUM(STICKY_BITS_HDA_LPBK_DIS, 0, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(STICKY_BITS_JTAG_STS, 6, ENABLE, DISABLE); + DEFINE_PMC_REG_BIT_ENUM(CNTRL2_WAKE_DET_EN, 9, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(SEC_DISABLE2_WRITE21, 26, OFF, ON); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp index d6827adeb..83ea3f677 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp @@ -17,6 +17,7 @@ #include <vapours.hpp> #define SB_CSR (0x200) +#define SB_PFCFG (0x208) #define SB_AA64_RESET_LOW (0x230) #define SB_AA64_RESET_HIGH (0x234) @@ -39,3 +40,8 @@ DEFINE_SB_REG_BIT_ENUM(CSR_HANG, 6, DISABLE, ENABLE); DEFINE_SB_REG_BIT_ENUM(CSR_SWDM_ENABLE, 7, DISABLE, ENABLE); DEFINE_SB_REG(CSR_SWDM_FAIL_COUNT, 8, 4); DEFINE_SB_REG(CSR_COT_FAIL_COUNT, 12, 4); + +DEFINE_SB_REG_BIT_ENUM(PFCFG_SPNIDEN, 0, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(PFCFG_SPIDEN, 1, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(PFCFG_NIDEN, 2, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(PFCFG_DBGEN, 3, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp index 93a7c8b66..ee3fe3dd1 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp @@ -16,7 +16,7 @@ #pragma once #include <vapours.hpp> - +#define TIMERUS_USEC_CFG (0x014) #define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4) #define TIMER_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (TIMER, NAME) @@ -30,6 +30,9 @@ #define DEFINE_TIMER_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) #define DEFINE_TIMER_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) +DEFINE_TIMER_REG(USEC_CFG_USEC_DIVISOR, 0, 8); +DEFINE_TIMER_REG(USEC_CFG_USEC_DIVIDEND, 8, 8); + DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, 5, DISABLE, ENABLE); DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, 6, DISABLE, ENABLE); DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, 7, DISABLE, ENABLE); diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index 920976f9e..7265e42d9 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -20,6 +20,11 @@ namespace ams::fuse { namespace { + struct BypassEntry { + u32 offset; + u32 value; + }; + struct OdmWord2 { using DeviceUniqueKeyGeneration = util::BitPack32::Field<0, 5, int>; using Reserved = util::BitPack32::Field<5, 27, int>; @@ -135,6 +140,12 @@ namespace ams::fuse { constexpr inline int NumFuseIncrements = util::size(FuseVersionIncrementFirmwares); + constexpr const BypassEntry FuseBypassEntries[] = { + /* Don't configure any fuse bypass entries. */ + }; + + constexpr inline int NumFuseBypassEntries = util::size(FuseBypassEntries); + /* Verify that the fuse version increment list is sorted. */ static_assert([] { for (size_t i = 0; i < util::size(FuseVersionIncrementFirmwares) - 1; ++i) { @@ -169,7 +180,7 @@ namespace ams::fuse { } void Lockout() { - reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE)); + reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, ENABLE)); } u32 ReadWord(int address) { @@ -367,4 +378,49 @@ namespace ams::fuse { return g_has_rcm_bug_patch; } + bool IsOdmProductionMode() { + return reg::HasValue(GetChipRegisters().FUSE_SECURITY_MODE, FUSE_REG_BITS_ENUM(SECURITY_MODE_SECURITY_MODE, ENABLED)); + } + + void ConfigureFuseBypass() { + /* Make the fuse registers visible. */ + clkrst::SetFuseVisibility(true); + + /* Only perform bypass configuration if fuse programming is allowed. */ + if (!reg::HasValue(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, DISABLE))) { + return; + } + + /* Enable software writes to fuses. */ + reg::ReadWrite(GetRegisters().FUSE_WRITE_ACCESS_SW, FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_CTRL, READWRITE), + FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_STATUS, WRITE)); + + /* Enable fuse bypass. */ + reg::Write(GetRegisters().FUSE_FUSEBYPASS, FUSE_REG_BITS_ENUM(FUSEBYPASS_VAL, ENABLE)); + + /* Override fuses. */ + for (const auto &entry : FuseBypassEntries) { + reg::Write(g_register_address + entry.offset, entry.value); + } + + /* Disable software writes to fuses. */ + reg::ReadWrite(GetRegisters().FUSE_WRITE_ACCESS_SW, FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_CTRL, READONLY)); + + /* NOTE: Here, NVidia almost certainly intends to *disable* fuse bypass, but they write enable instead... */ + reg::Write(GetRegisters().FUSE_FUSEBYPASS, FUSE_REG_BITS_ENUM(FUSEBYPASS_VAL, ENABLE)); + + /* NOTE: Here, NVidia intends to disable fuse programming. However, they fuck up -- and *clear* the disable bit. */ + /* It should be noted that this is a sticky bit, and thus software clears have no effect. */ + reg::ReadWrite(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, DISABLE)); + + /* Configure FUSE_PRIVATEKEYDISABLE_TZ_STICKY_BIT. */ + constexpr const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + const bool key_invisible = reg::HasValue(PMC + APBDEV_PMC_SECURE_SCRATCH21, FUSE_REG_BITS_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, KEY_INVISIBLE)); + + reg::ReadWrite(GetRegisters().FUSE_PRIVATEKEYDISABLE, FUSE_REG_BITS_ENUM_SEL(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, key_invisible, KEY_INVISIBLE, KEY_VISIBLE)); + + /* Write-lock PMC_SECURE_SCRATCH21. */ + reg::ReadWrite(PMC + APBDEV_PMC_SEC_DISABLE2, PMC_REG_BITS_ENUM(SEC_DISABLE2_WRITE21, ON)); + } + } diff --git a/libraries/libexosphere/source/fuse/fuse_registers.hpp b/libraries/libexosphere/source/fuse/fuse_registers.hpp index ff0fcc571..3d729c9b6 100644 --- a/libraries/libexosphere/source/fuse/fuse_registers.hpp +++ b/libraries/libexosphere/source/fuse/fuse_registers.hpp @@ -241,6 +241,13 @@ namespace ams::fuse { DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, 4, KEY_VISIBLE, KEY_INVISIBLE); DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_PRIVATEKEYDISABLE_VAL_KEY, 0, VISIBLE, INVISIBLE); - DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE); + DEFINE_FUSE_REG_BIT_ENUM(FUSEBYPASS_VAL, 0, DISABLE, ENABLE); + + DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE); + + DEFINE_FUSE_REG_BIT_ENUM(WRITE_ACCESS_SW_CTRL, 0, READWRITE, READONLY); + DEFINE_FUSE_REG_BIT_ENUM(WRITE_ACCESS_SW_STATUS, 16, NOWRITE, WRITE); + + DEFINE_FUSE_REG_BIT_ENUM(SECURITY_MODE_SECURITY_MODE, 0, DISABLED, ENABLED); } diff --git a/libraries/libexosphere/source/pinmux/pinmux_api.cpp b/libraries/libexosphere/source/pinmux/pinmux_api.cpp index 513797dfb..276c9c8b8 100644 --- a/libraries/libexosphere/source/pinmux/pinmux_api.cpp +++ b/libraries/libexosphere/source/pinmux/pinmux_api.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <exosphere.hpp> -#include "pinmux_registers.hpp" namespace ams::pinmux { diff --git a/libraries/libexosphere/source/pinmux/pinmux_registers.hpp b/libraries/libexosphere/source/pinmux/pinmux_registers.hpp deleted file mode 100644 index 4fd1c4d51..000000000 --- a/libraries/libexosphere/source/pinmux/pinmux_registers.hpp +++ /dev/null @@ -1,65 +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 <exosphere.hpp> - -namespace ams::pinmux { - - #define PINMUX_AUX_GEN1_I2C_SCL (0x30BC) - #define PINMUX_AUX_GEN1_I2C_SDA (0x30C0) - #define PINMUX_AUX_PWR_I2C_SCL (0x30DC) - #define PINMUX_AUX_PWR_I2C_SDA (0x30E0) - - #define PINMUX_AUX_UART1_TX (0x30E4) - #define PINMUX_AUX_UART1_RX (0x30E8) - #define PINMUX_AUX_UART1_RTS (0x30EC) - #define PINMUX_AUX_UART1_CTS (0x30F0) - #define PINMUX_AUX_UART2_TX (0x30F4) - #define PINMUX_AUX_UART2_RX (0x30F8) - #define PINMUX_AUX_UART2_RTS (0x30FC) - #define PINMUX_AUX_UART2_CTS (0x3100) - #define PINMUX_AUX_UART3_TX (0x3104) - #define PINMUX_AUX_UART3_RX (0x3108) - #define PINMUX_AUX_UART3_RTS (0x310C) - #define PINMUX_AUX_UART3_CTS (0x3110) - - #define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME) - #define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE) - #define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM) - #define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) - - #define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__) - #define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE) - #define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) - #define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) - #define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) - - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE); - DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE); - - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3); - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3); - - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3); - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART); - DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3); - -} diff --git a/libraries/libexosphere/source/pmic/pmic_api.cpp b/libraries/libexosphere/source/pmic/pmic_api.cpp index bf1515d7c..808d465d0 100644 --- a/libraries/libexosphere/source/pmic/pmic_api.cpp +++ b/libraries/libexosphere/source/pmic/pmic_api.cpp @@ -60,6 +60,10 @@ namespace ams::pmic { i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterGpio0 + gpio, MAX77620_CNFG_GPIO_DRV_PUSHPULL | MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH); } + void SetEnBitErista() { + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_ENABLE); + } + void EnableVddCpuErista() { /* Enable GPIO 5. */ /* TODO: What does this control? */ @@ -87,6 +91,11 @@ namespace ams::pmic { } } + void SetEnBitMariko(Regulator regulator) { + /* Set EN_M3_LPM to enable BUCK Master 3 low power mode. */ + i2c::SendByte(i2c::Port_5, GetI2cAddressForMarikoMax77812(regulator), Max77812RegisterEnCtrl, 0x40); + } + void EnableVddCpuMariko(Regulator regulator) { const int address = GetI2cAddressForMarikoMax77812(regulator); @@ -118,6 +127,17 @@ namespace ams::pmic { } + void SetEnBit(Regulator regulator) { + switch (regulator) { + case Regulator_Erista_Max77621: + return SetEnBitErista(); + case Regulator_Mariko_Max77812_A: + case Regulator_Mariko_Max77812_B: + return SetEnBitMariko(regulator); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + void EnableVddCpu(Regulator regulator) { switch (regulator) { case Regulator_Erista_Max77621: diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index 5ded61a13..00105f71d 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -308,6 +308,37 @@ namespace ams::se { ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size); } + void DecryptAesCbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size, AesMode mode) { + /* If nothing to decrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine extents. */ + const size_t num_blocks = src_size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + AMS_ABORT_UNLESS(src_size == aligned_size); + + /* Configure for aes-cbc encryption. */ + SetConfig(SE, false, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, false, AesConfigCbcDecrypt); + UpdateAesMode(SE, mode); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Set the block count. */ + SetBlockCount(SE, num_blocks); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size); + } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { /* If nothing to decrypt, succeed. */ if (size == 0) { return; } @@ -349,6 +380,35 @@ namespace ams::se { } } + void ClearAesKeyIv(int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set each iv word in order. */ + for (int i = 0; i < 4; ++i) { + /* Select the keyslot original iv. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the iv word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + + /* Select the keyslot updated iv. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, UPDATED_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the iv word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + } + void LockAesKeySlot(int slot, u32 flags) { /* Validate the key slot. */ AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); @@ -487,6 +547,14 @@ namespace ams::se { return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256); } + void DecryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return DecryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes128); + } + + void DecryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return DecryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256); + } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { /* Validate the iv. */ AMS_ABORT_UNLESS(iv_size == AesBlockSize); diff --git a/libraries/libvapours/source/crypto/crypto_memory_compare.arch.arm.cpp b/libraries/libvapours/source/crypto/crypto_memory_compare.arch.arm.cpp new file mode 100644 index 000000000..340a2fe52 --- /dev/null +++ b/libraries/libvapours/source/crypto/crypto_memory_compare.arch.arm.cpp @@ -0,0 +1,58 @@ +/* + * 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 <vapours.hpp> + +namespace ams::crypto { + + bool IsSameBytes(const void *lhs, const void *rhs, size_t size) { + bool result; + u8 xor_acc, ltmp, rtmp; + size_t index; + + __asm__ __volatile__( + /* Clear registers and prepare for comparison. */ + " movs %[xor_acc], #0\n" + " movs %[index], #0\n" + " b 1f\n" + + /* Compare one byte in constant time. */ + "0:\n" + " ldrb %[ltmp], [%[lhs]]\n" + " ldrb %[rtmp], [%[rhs]]\n" + " adds %[lhs], #1\n" + " adds %[rhs], #1\n" + " eors %[ltmp], %[ltmp], %[rtmp]\n" + " orrs %[xor_acc], %[xor_acc], %[ltmp]\n" + " adds %[index], #1\n" + + /* Check if there is still data to compare. */ + "1:\n" + " cmp %[index], %[size]\n" + " bcc 0b\n" + + /* We're done, set result. */ + " cmp %[xor_acc], #0\n" + " moveq %[result], #1\n" + " movne %[result], #0\n" + : [result]"=r"(result), [lhs]"+r"(lhs), [rhs]"+r"(rhs), [xor_acc]"=&r"(xor_acc), [index]"=&r"(index), [ltmp]"=&r"(ltmp), [rtmp]"=&r"(rtmp) + : [size]"r"(size) + : "cc" + ); + + return result; + } + +} \ No newline at end of file From c75e61a40b3166f098e3935ac0eb579e6dbcee31 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 01:48:36 -0700 Subject: [PATCH 091/118] fusee: switch to use exo2 instead of exo1 --- Makefile | 11 ++++++----- exosphere2/Makefile | 19 ++++++++++++------- fusee/fusee-primary/Makefile | 6 +++--- fusee/fusee-secondary/Makefile | 8 ++++---- fusee/fusee-secondary/linker.ld | 4 ++-- fusee/fusee-secondary/src/nxboot.c | 10 +++++----- fusee/fusee-secondary/src/start.s | 8 ++++---- sept/sept-secondary/Makefile | 6 +++--- 8 files changed, 39 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index b1f77f19f..2e3302b9d 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ thermosphere: $(MAKE) -C thermosphere all exosphere: thermosphere - $(MAKE) -C exosphere all + $(MAKE) -C exosphere2 all stratosphere: exosphere libraries $(MAKE) -C stratosphere all @@ -120,10 +120,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 exosphere2/loader_stub/loader_stub.elf atmosphere-$(AMSVER)-debug/exosphere-loader-stub.elf + cp exosphere2/program/program.elf atmosphere-$(AMSVER)-debug/exosphere-program.elf + cp exosphere2/warmboot/warmboot.elf atmosphere-$(AMSVER)-debug/exosphere-warmboot.elf + cp exosphere2/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf + cp exosphere2/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 diff --git a/exosphere2/Makefile b/exosphere2/Makefile index 20619aed9..8017acdef 100644 --- a/exosphere2/Makefile +++ b/exosphere2/Makefile @@ -1,9 +1,9 @@ -TARGETS := exosphere.bin program.lz4 -CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean +TARGETS := exosphere.bin warmboot.bin program.lz4 +CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean warmboot-clean SUBFOLDERS := $(MODULES) -all: exosphere.bin +all: exosphere.bin warmboot.bin clean: $(CLEAN_TARGETS) @rm -f exosphere.bin @@ -14,15 +14,16 @@ exosphere.bin: program.lz4 boot_code.lz4 @printf LENY >> exosphere.bin @echo "Built exosphere.bin..." -program.lz4: check_libexo +program.lz4: $(MAKE) -C program @cp program/program.lz4 program.lz4 @cp program/boot_code.lz4 boot_code.lz4 -boot_code.lz4: program.lz4 +warmboot.bin: + $(MAKE) -C warmboot + @cp warmboot/warmboot.bin warmboot.bin -check_libexo: - @$(MAKE) --no-print-directory -C ../libraries/libexosphere +boot_code.lz4: program.lz4 exosphere-clean: $(MAKE) -C loader_stub clean @@ -32,6 +33,10 @@ program-clean: $(MAKE) -C program clean @rm -f program.lz4 +warmboot-clean: + $(MAKE) -C warmboot clean + @rm -f warmboot.bin + boot_code-clean: @rm -f boot_code.lz4 diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index 3124e7925..dd3828792 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -77,7 +77,7 @@ export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere/rebootstub + $(AMS)/exosphere2/program/rebootstub export DEPSDIR := $(CURDIR)/$(BUILD) @@ -117,7 +117,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) all: check_rebootstub $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere/rebootstub all + @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all $(BUILD): check_rebootstub @[ -d $@ ] || mkdir -p $@ @@ -126,7 +126,7 @@ $(BUILD): check_rebootstub #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index af50dc965..b2798e1a1 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -87,7 +87,7 @@ export TOPDIR := $(CURDIR) export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \ + $(AMS)/exosphere2 $(AMS)/exosphere2/warmboot $(AMS)/exosphere2/program/rebootstub \ $(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \ $(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere/kernel_ldr $(KIPDIRS) @@ -98,7 +98,7 @@ CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \ - exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ + exosphere.bin warmboot.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \ sept-secondary_dev_00.enc sept-secondary_dev_01.enc kernel_ldr.bin $(KIPFILES) @@ -137,7 +137,7 @@ check_fusee_primary: @$(MAKE) -C $(AMS)/fusee/fusee-primary all check_exosphere: - @$(MAKE) -C $(AMS)/exosphere all + @$(MAKE) -C $(AMS)/exosphere2 all check_sept: @$(MAKE) -C $(AMS)/sept all @@ -166,7 +166,7 @@ $(BUILD): check_fusee_primary check_exosphere check_sept check_emummc check_ther clean: @echo clean ... @$(MAKE) -C $(AMS)/fusee/fusee-primary clean - @$(MAKE) -C $(AMS)/exosphere clean + @$(MAKE) -C $(AMS)/exosphere2 clean @$(MAKE) -C $(AMS)/thermosphere clean @$(MAKE) -C $(AMS)/libraries clean @$(MAKE) -C $(AMS)/mesosphere clean diff --git a/fusee/fusee-secondary/linker.ld b/fusee/fusee-secondary/linker.ld index 4ca97eb8f..610ff743b 100644 --- a/fusee/fusee-secondary/linker.ld +++ b/fusee/fusee-secondary/linker.ld @@ -228,8 +228,8 @@ SECTIONS PROVIDE(__fusee_primary_bin_size__ = fusee_primary_bin_end - fusee_primary_bin); PROVIDE(__loader_kip_start__ = loader_kip - __start__); PROVIDE(__loader_kip_size__ = loader_kip_end - loader_kip); - PROVIDE(__lp0fw_bin_start__ = lp0fw_bin - __start__); - PROVIDE(__lp0fw_bin_size__ = lp0fw_bin_end - lp0fw_bin); + PROVIDE(__warmboot_bin_start__ = warmboot_bin - __start__); + PROVIDE(__warmboot_bin_size__ = warmboot_bin_end - warmboot_bin); PROVIDE(__ncm_kip_start__ = ncm_kip - __start__); PROVIDE(__ncm_kip_size__ = ncm_kip_end - ncm_kip); PROVIDE(__pm_kip_start__ = pm_kip - __start__); diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 28ec5fb34..45a0bca02 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -58,13 +58,13 @@ #include "sept_secondary_01_enc.h" #include "sept_secondary_dev_00_enc.h" #include "sept_secondary_dev_01_enc.h" -#include "lp0fw_bin.h" +#include "warmboot_bin.h" #include "emummc_kip.h" #undef u8 #undef u32 -extern const uint8_t lp0fw_bin[]; -extern const uint32_t lp0fw_bin_size; +extern const uint8_t warmboot_bin[]; +extern const uint32_t warmboot_bin_size; static const uint8_t retail_pkc_modulus[0x100] = { 0xF7, 0x86, 0x47, 0xAB, 0x71, 0x89, 0x81, 0xB5, 0xCF, 0x0C, 0xB0, 0xE8, 0x48, 0xA7, 0xFD, 0xAD, @@ -891,14 +891,14 @@ uint32_t nxboot_main(void) { } } else { /* Use Atmosphere's warmboot firmware implementation. */ - warmboot_fw_size = lp0fw_bin_size; + warmboot_fw_size = warmboot_bin_size; warmboot_fw = malloc(warmboot_fw_size); if (warmboot_fw == NULL) { fatal_error("[NXBOOT] Out of memory!\n"); } - memcpy(warmboot_fw, lp0fw_bin, warmboot_fw_size); + memcpy(warmboot_fw, warmboot_bin, warmboot_fw_size); if (warmboot_fw_size == 0) { fatal_error("[NXBOOT] Could not read the warmboot firmware from Package1!\n"); diff --git a/fusee/fusee-secondary/src/start.s b/fusee/fusee-secondary/src/start.s index 1b88db9fe..0050b053d 100644 --- a/fusee/fusee-secondary/src/start.s +++ b/fusee/fusee-secondary/src/start.s @@ -157,15 +157,15 @@ _content_headers: .asciz "Loader" .align 5 -/* lp0fw content header */ -.word __lp0fw_bin_start__ -.word __lp0fw_bin_size__ +/* warmboot content header */ +.word __warmboot_bin_start__ +.word __warmboot_bin_size__ .byte CONTENT_TYPE_WBT .byte CONTENT_FLAG_NONE .byte CONTENT_FLAG_NONE .byte CONTENT_FLAG_NONE .word 0xCCCCCCCC -.asciz "lp0fw" +.asciz "warmboot" .align 5 /* pm content header */ diff --git a/sept/sept-secondary/Makefile b/sept/sept-secondary/Makefile index 30a2d08a2..e8567f0dd 100644 --- a/sept/sept-secondary/Makefile +++ b/sept/sept-secondary/Makefile @@ -77,7 +77,7 @@ export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere/rebootstub \ + $(AMS)/exosphere2/program/rebootstub \ $(TOPDIR)/key_derivation export DEPSDIR := $(CURDIR)/$(BUILD) @@ -118,7 +118,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) all: check_rebootstub check_key_derivation $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere/rebootstub all + @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all check_key_derivation: @$(MAKE) -C key_derivation @@ -138,7 +138,7 @@ endif #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean @$(MAKE) -C key_derivation clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET)_*.enc $(TARGET).elf From 282f8f6612e98153bdedf22791f4508560f3bfd6 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 01:49:41 -0700 Subject: [PATCH 092/118] ams: delete exo1 --- exosphere/Makefile | 180 ----- exosphere/README.md | 6 - exosphere/linker.ld | 269 ------- exosphere/linker.specs | 4 - exosphere/lp0fw/Makefile | 154 ---- exosphere/lp0fw/linker.ld | 24 - exosphere/lp0fw/linker.specs | 7 - exosphere/lp0fw/src/car.c | 138 ---- exosphere/lp0fw/src/car.h | 110 --- exosphere/lp0fw/src/cluster.c | 163 ---- exosphere/lp0fw/src/cluster.h | 30 - exosphere/lp0fw/src/emc.c | 33 - exosphere/lp0fw/src/emc.h | 71 -- exosphere/lp0fw/src/flow.c | 31 - exosphere/lp0fw/src/flow.h | 35 - exosphere/lp0fw/src/fuse.c | 75 -- exosphere/lp0fw/src/fuse.h | 213 ----- exosphere/lp0fw/src/i2c.c | 76 -- exosphere/lp0fw/src/i2c.h | 48 -- exosphere/lp0fw/src/lp0.c | 98 --- exosphere/lp0fw/src/lp0.h | 35 - exosphere/lp0fw/src/mc.c | 40 - exosphere/lp0fw/src/mc.h | 622 --------------- exosphere/lp0fw/src/misc.c | 52 -- exosphere/lp0fw/src/misc.h | 39 - exosphere/lp0fw/src/pmc.h | 61 -- exosphere/lp0fw/src/se.c | 266 ------- exosphere/lp0fw/src/se.h | 179 ----- exosphere/lp0fw/src/secmon.c | 129 --- exosphere/lp0fw/src/secmon.h | 26 - exosphere/lp0fw/src/start.s | 65 -- exosphere/lp0fw/src/sysreg.h | 46 -- exosphere/lp0fw/src/timer.h | 34 - exosphere/lp0fw/src/utils.h | 39 - exosphere/rebootstub/Makefile | 154 ---- exosphere/rebootstub/linker.ld | 18 - exosphere/rebootstub/linker.specs | 7 - exosphere/rebootstub/src/i2c.c | 169 ---- exosphere/rebootstub/src/i2c.h | 51 -- exosphere/rebootstub/src/max77620.h | 360 --------- exosphere/rebootstub/src/shutdown.c | 30 - exosphere/rebootstub/src/start.s | 50 -- exosphere/rebootstub/src/timer.h | 33 - exosphere/rebootstub/src/utils.h | 38 - exosphere/sc7fw/Makefile | 154 ---- exosphere/sc7fw/linker.ld | 21 - exosphere/sc7fw/linker.specs | 7 - exosphere/sc7fw/src/emc.c | 73 -- exosphere/sc7fw/src/emc.h | 67 -- exosphere/sc7fw/src/i2c.c | 104 --- exosphere/sc7fw/src/i2c.h | 50 -- exosphere/sc7fw/src/pmc.h | 53 -- exosphere/sc7fw/src/sc7.c | 120 --- exosphere/sc7fw/src/sc7.h | 26 - exosphere/sc7fw/src/start.s | 40 - exosphere/sc7fw/src/timer.h | 33 - exosphere/sc7fw/src/utils.h | 38 - exosphere/src/actmon.c | 40 - exosphere/src/actmon.h | 41 - exosphere/src/arm.h | 40 - exosphere/src/arm.s | 316 -------- exosphere/src/bootconfig.c | 164 ---- exosphere/src/bootconfig.h | 94 --- exosphere/src/bootup.c | 381 --------- exosphere/src/bootup.h | 115 --- exosphere/src/bpmp.h | 44 -- exosphere/src/car.c | 87 -- exosphere/src/car.h | 58 -- exosphere/src/coldboot_init.c | 213 ----- exosphere/src/coldboot_main.c | 36 - exosphere/src/configitem.c | 308 -------- exosphere/src/configitem.h | 71 -- exosphere/src/cpu_context.c | 235 ------ exosphere/src/cpu_context.h | 83 -- exosphere/src/emummc_cfg.h | 67 -- exosphere/src/exceptions.s | 254 ------ exosphere/src/exocfg.c | 124 --- exosphere/src/exocfg.h | 79 -- exosphere/src/flow.h | 69 -- exosphere/src/fuse.c | 335 -------- exosphere/src/fuse.h | 234 ------ exosphere/src/gcm.c | 229 ------ exosphere/src/gcm.h | 37 - exosphere/src/i2c.c | 252 ------ exosphere/src/i2c.h | 113 --- exosphere/src/interrupt.c | 131 --- exosphere/src/interrupt.h | 80 -- exosphere/src/masterkey.c | 156 ---- exosphere/src/masterkey.h | 49 -- exosphere/src/mc.c | 162 ---- exosphere/src/mc.h | 631 --------------- exosphere/src/mc0.h | 32 - exosphere/src/mc1.h | 32 - exosphere/src/memory_map.h | 234 ------ exosphere/src/misc.h | 32 - exosphere/src/mmu.h | 193 ----- exosphere/src/my_libc.c | 1141 --------------------------- exosphere/src/package2.c | 637 --------------- exosphere/src/package2.h | 121 --- exosphere/src/panic_color.h | 39 - exosphere/src/pinmux.h | 214 ----- exosphere/src/pmc.h | 65 -- exosphere/src/preprocessor.h | 207 ----- exosphere/src/randomcache.c | 91 --- exosphere/src/randomcache.h | 29 - exosphere/src/rsa_common.c | 20 - exosphere/src/rsa_common.h | 36 - exosphere/src/sc7.c | 305 ------- exosphere/src/sc7.h | 28 - exosphere/src/se.c | 912 --------------------- exosphere/src/se.h | 231 ------ exosphere/src/sealedkeys.c | 91 --- exosphere/src/sealedkeys.h | 41 - exosphere/src/smc_ams.c | 290 ------- exosphere/src/smc_ams.h | 30 - exosphere/src/smc_api.c | 776 ------------------ exosphere/src/smc_api.h | 57 -- exosphere/src/smc_user.c | 1019 ------------------------ exosphere/src/smc_user.h | 47 -- exosphere/src/start.s | 300 ------- exosphere/src/synchronization.h | 139 ---- exosphere/src/sysctr0.h | 49 -- exosphere/src/sysreg.h | 49 -- exosphere/src/timers.c | 41 - exosphere/src/timers.h | 111 --- exosphere/src/titlekey.c | 180 ----- exosphere/src/titlekey.h | 33 - exosphere/src/uart.c | 135 ---- exosphere/src/uart.h | 178 ----- exosphere/src/userpage.c | 93 --- exosphere/src/userpage.h | 39 - exosphere/src/utils.c | 80 -- exosphere/src/utils.h | 134 ---- exosphere/src/warmboot_init.c | 221 ------ exosphere/src/warmboot_main.c | 108 --- 135 files changed, 19262 deletions(-) delete mode 100644 exosphere/Makefile delete mode 100644 exosphere/README.md delete mode 100644 exosphere/linker.ld delete mode 100644 exosphere/linker.specs delete mode 100644 exosphere/lp0fw/Makefile delete mode 100644 exosphere/lp0fw/linker.ld delete mode 100644 exosphere/lp0fw/linker.specs delete mode 100644 exosphere/lp0fw/src/car.c delete mode 100644 exosphere/lp0fw/src/car.h delete mode 100644 exosphere/lp0fw/src/cluster.c delete mode 100644 exosphere/lp0fw/src/cluster.h delete mode 100644 exosphere/lp0fw/src/emc.c delete mode 100644 exosphere/lp0fw/src/emc.h delete mode 100644 exosphere/lp0fw/src/flow.c delete mode 100644 exosphere/lp0fw/src/flow.h delete mode 100644 exosphere/lp0fw/src/fuse.c delete mode 100644 exosphere/lp0fw/src/fuse.h delete mode 100644 exosphere/lp0fw/src/i2c.c delete mode 100644 exosphere/lp0fw/src/i2c.h delete mode 100644 exosphere/lp0fw/src/lp0.c delete mode 100644 exosphere/lp0fw/src/lp0.h delete mode 100644 exosphere/lp0fw/src/mc.c delete mode 100644 exosphere/lp0fw/src/mc.h delete mode 100644 exosphere/lp0fw/src/misc.c delete mode 100644 exosphere/lp0fw/src/misc.h delete mode 100644 exosphere/lp0fw/src/pmc.h delete mode 100644 exosphere/lp0fw/src/se.c delete mode 100644 exosphere/lp0fw/src/se.h delete mode 100644 exosphere/lp0fw/src/secmon.c delete mode 100644 exosphere/lp0fw/src/secmon.h delete mode 100644 exosphere/lp0fw/src/start.s delete mode 100644 exosphere/lp0fw/src/sysreg.h delete mode 100644 exosphere/lp0fw/src/timer.h delete mode 100644 exosphere/lp0fw/src/utils.h delete mode 100644 exosphere/rebootstub/Makefile delete mode 100644 exosphere/rebootstub/linker.ld delete mode 100644 exosphere/rebootstub/linker.specs delete mode 100644 exosphere/rebootstub/src/i2c.c delete mode 100644 exosphere/rebootstub/src/i2c.h delete mode 100644 exosphere/rebootstub/src/max77620.h delete mode 100644 exosphere/rebootstub/src/shutdown.c delete mode 100644 exosphere/rebootstub/src/start.s delete mode 100644 exosphere/rebootstub/src/timer.h delete mode 100644 exosphere/rebootstub/src/utils.h delete mode 100644 exosphere/sc7fw/Makefile delete mode 100644 exosphere/sc7fw/linker.ld delete mode 100644 exosphere/sc7fw/linker.specs delete mode 100644 exosphere/sc7fw/src/emc.c delete mode 100644 exosphere/sc7fw/src/emc.h delete mode 100644 exosphere/sc7fw/src/i2c.c delete mode 100644 exosphere/sc7fw/src/i2c.h delete mode 100644 exosphere/sc7fw/src/pmc.h delete mode 100644 exosphere/sc7fw/src/sc7.c delete mode 100644 exosphere/sc7fw/src/sc7.h delete mode 100644 exosphere/sc7fw/src/start.s delete mode 100644 exosphere/sc7fw/src/timer.h delete mode 100644 exosphere/sc7fw/src/utils.h delete mode 100644 exosphere/src/actmon.c delete mode 100644 exosphere/src/actmon.h delete mode 100644 exosphere/src/arm.h delete mode 100644 exosphere/src/arm.s delete mode 100644 exosphere/src/bootconfig.c delete mode 100644 exosphere/src/bootconfig.h delete mode 100644 exosphere/src/bootup.c delete mode 100644 exosphere/src/bootup.h delete mode 100644 exosphere/src/bpmp.h delete mode 100644 exosphere/src/car.c delete mode 100644 exosphere/src/car.h delete mode 100644 exosphere/src/coldboot_init.c delete mode 100644 exosphere/src/coldboot_main.c delete mode 100644 exosphere/src/configitem.c delete mode 100644 exosphere/src/configitem.h delete mode 100644 exosphere/src/cpu_context.c delete mode 100644 exosphere/src/cpu_context.h delete mode 100644 exosphere/src/emummc_cfg.h delete mode 100644 exosphere/src/exceptions.s delete mode 100644 exosphere/src/exocfg.c delete mode 100644 exosphere/src/exocfg.h delete mode 100644 exosphere/src/flow.h delete mode 100644 exosphere/src/fuse.c delete mode 100644 exosphere/src/fuse.h delete mode 100644 exosphere/src/gcm.c delete mode 100644 exosphere/src/gcm.h delete mode 100644 exosphere/src/i2c.c delete mode 100644 exosphere/src/i2c.h delete mode 100644 exosphere/src/interrupt.c delete mode 100644 exosphere/src/interrupt.h delete mode 100644 exosphere/src/masterkey.c delete mode 100644 exosphere/src/masterkey.h delete mode 100644 exosphere/src/mc.c delete mode 100644 exosphere/src/mc.h delete mode 100644 exosphere/src/mc0.h delete mode 100644 exosphere/src/mc1.h delete mode 100644 exosphere/src/memory_map.h delete mode 100644 exosphere/src/misc.h delete mode 100644 exosphere/src/mmu.h delete mode 100644 exosphere/src/my_libc.c delete mode 100644 exosphere/src/package2.c delete mode 100644 exosphere/src/package2.h delete mode 100644 exosphere/src/panic_color.h delete mode 100644 exosphere/src/pinmux.h delete mode 100644 exosphere/src/pmc.h delete mode 100644 exosphere/src/preprocessor.h delete mode 100644 exosphere/src/randomcache.c delete mode 100644 exosphere/src/randomcache.h delete mode 100644 exosphere/src/rsa_common.c delete mode 100644 exosphere/src/rsa_common.h delete mode 100644 exosphere/src/sc7.c delete mode 100644 exosphere/src/sc7.h delete mode 100644 exosphere/src/se.c delete mode 100644 exosphere/src/se.h delete mode 100644 exosphere/src/sealedkeys.c delete mode 100644 exosphere/src/sealedkeys.h delete mode 100644 exosphere/src/smc_ams.c delete mode 100644 exosphere/src/smc_ams.h delete mode 100644 exosphere/src/smc_api.c delete mode 100644 exosphere/src/smc_api.h delete mode 100644 exosphere/src/smc_user.c delete mode 100644 exosphere/src/smc_user.h delete mode 100644 exosphere/src/start.s delete mode 100644 exosphere/src/synchronization.h delete mode 100644 exosphere/src/sysctr0.h delete mode 100644 exosphere/src/sysreg.h delete mode 100644 exosphere/src/timers.c delete mode 100644 exosphere/src/timers.h delete mode 100644 exosphere/src/titlekey.c delete mode 100644 exosphere/src/titlekey.h delete mode 100644 exosphere/src/uart.c delete mode 100644 exosphere/src/uart.h delete mode 100644 exosphere/src/userpage.c delete mode 100644 exosphere/src/userpage.h delete mode 100644 exosphere/src/utils.c delete mode 100644 exosphere/src/utils.h delete mode 100644 exosphere/src/warmboot_init.c delete mode 100644 exosphere/src/warmboot_main.c diff --git a/exosphere/Makefile b/exosphere/Makefile deleted file mode 100644 index 14a03900d..000000000 --- a/exosphere/Makefile +++ /dev/null @@ -1,180 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITPRO)),) -$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITPRO)/devkitA64/base_rules - -AMSBRANCH := $(shell git symbolic-ref --short HEAD) -AMSHASH = $(shell git rev-parse --short=16 HEAD) -AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) - -ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) - AMSREV := $(AMSREV)-dirty -endif - -#--------------------------------------------------------------------------------- -# 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 - -#--------------------------------------------------------------------------------- -# 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) - -CFLAGS += $(INCLUDE) - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -nostartfiles -nostdlib -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := - - -#--------------------------------------------------------------------------------- -# 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 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) - -my_libc.o: CFLAGS += -fno-builtin - -%.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 -#--------------------------------------------------------------------------------------- diff --git a/exosphere/README.md b/exosphere/README.md deleted file mode 100644 index b4b8039cb..000000000 --- a/exosphere/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Exosphère -===== - -![License](https://img.shields.io/badge/License-GPLv2-blue.svg) - -Exosphère is a Secure Monitor implementation for the Nintendo Switch. diff --git a/exosphere/linker.ld b/exosphere/linker.ld deleted file mode 100644 index e1a694ea5..000000000 --- a/exosphere/linker.ld +++ /dev/null @@ -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) } -} \ No newline at end of file diff --git a/exosphere/linker.specs b/exosphere/linker.specs deleted file mode 100644 index fb0cb34bc..000000000 --- a/exosphere/linker.specs +++ /dev/null @@ -1,4 +0,0 @@ -%rename link old_link - -*link: -%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections diff --git a/exosphere/lp0fw/Makefile b/exosphere/lp0fw/Makefile deleted file mode 100644 index a1073d35e..000000000 --- a/exosphere/lp0fw/Makefile +++ /dev/null @@ -1,154 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/base_rules - -#--------------------------------------------------------------------------------- -# 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 -DATA := data -INCLUDES := include ../../libraries/libvapours/include - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork - -CFLAGS := \ - -g \ - -Os \ - -ffunction-sections \ - -fdata-sections \ - -fomit-frame-pointer \ - -fno-inline \ - -std=gnu11 \ - -Werror \ - -Wall \ - $(ARCH) $(DEFINES) - -CFLAGS += $(INCLUDE) -D__BPMP__ - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := - - -#--------------------------------------------------------------------------------- -# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -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)/*.*))) - -#--------------------------------------------------------------------------------- -# 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) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) - -$(BUILD): - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#--------------------------------------------------------------------------------- -clean: - @echo 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: $(OFILES) - @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 -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere/lp0fw/linker.ld b/exosphere/lp0fw/linker.ld deleted file mode 100644 index 165608360..000000000 --- a/exosphere/lp0fw/linker.ld +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/exosphere/lp0fw/linker.specs b/exosphere/lp0fw/linker.specs deleted file mode 100644 index 300990418..000000000 --- a/exosphere/lp0fw/linker.specs +++ /dev/null @@ -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 diff --git a/exosphere/lp0fw/src/car.c b/exosphere/lp0fw/src/car.c deleted file mode 100644 index 03bc687cd..000000000 --- a/exosphere/lp0fw/src/car.c +++ /dev/null @@ -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)); -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/car.h b/exosphere/lp0fw/src/car.h deleted file mode 100644 index 509672d1c..000000000 --- a/exosphere/lp0fw/src/car.h +++ /dev/null @@ -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 diff --git a/exosphere/lp0fw/src/cluster.c b/exosphere/lp0fw/src/cluster.c deleted file mode 100644 index 71692f2cd..000000000 --- a/exosphere/lp0fw/src/cluster.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/cluster.h b/exosphere/lp0fw/src/cluster.h deleted file mode 100644 index e8db7cfbb..000000000 --- a/exosphere/lp0fw/src/cluster.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 naehrwert - * 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_CLUSTER_H -#define EXOSPHERE_WARMBOOT_BIN_CLUSTER_H - -#include <stdint.h> - -#include "utils.h" - -#define MSELECT_CONFIG_0 MAKE_REG32(0x50060000) - -void cluster_initialize_cpu(void); -void cluster_power_on_cpu(void); - -#endif diff --git a/exosphere/lp0fw/src/emc.c b/exosphere/lp0fw/src/emc.c deleted file mode 100644 index 91888920d..000000000 --- a/exosphere/lp0fw/src/emc.c +++ /dev/null @@ -1,33 +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 "utils.h" -#include "lp0.h" -#include "emc.h" -#include "pmc.h" -#include "timer.h" - -void emc_configure_pmacro_training(void) { - /* Set DISABLE_CFG_BYTEN for all N. */ - EMC_PMACRO_CFG_PM_GLOBAL_0_0 = 0xFF0000; - - /* Set CHN_TRAINING_E_WRPTR for channel 0 + channel 1. */ - EMC_PMACRO_TRAINING_CTRL_0_0 = 8; - EMC_PMACRO_TRAINING_CTRL_1_0 = 8; - - /* Clear DISABLE_CFG_BYTEN for all N. */ - EMC_PMACRO_CFG_PM_GLOBAL_0_0 = 0x0; -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/emc.h b/exosphere/lp0fw/src/emc.h deleted file mode 100644 index f8643e1ad..000000000 --- a/exosphere/lp0fw/src/emc.h +++ /dev/null @@ -1,71 +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_EMC_H -#define EXOSPHERE_WARMBOOT_BIN_EMC_H - -#include "utils.h" - -#define EMC_BASE (0x7001B000) - -#define EMC0_BASE (0x7001E000) -#define EMC1_BASE (0x7001F000) - - -#define MAKE_EMC_REG(ofs) (MAKE_REG32(EMC_BASE + ofs)) - -#define MAKE_EMC0_REG(ofs) (MAKE_REG32(EMC0_BASE + ofs)) -#define MAKE_EMC1_REG(ofs) (MAKE_REG32(EMC1_BASE + ofs)) - -#define EMC_CFG_0 MAKE_EMC_REG(0x00C) - -#define EMC_ADR_CFG_0 MAKE_EMC_REG(0x10) - -#define EMC_TIMING_CONTROL_0 MAKE_EMC_REG(0x028) - -#define EMC_SELF_REF_0 MAKE_EMC_REG(0x0E0) - -#define EMC_MRW_0 MAKE_EMC_REG(0x0E8) - -#define EMC_FBIO_CFG5_0 MAKE_EMC_REG(0x104) - -#define EMC_MRW3_0 MAKE_EMC_REG(0x138) - -#define EMC_AUTO_CAL_CONFIG_0 MAKE_EMC_REG(0x2A4) - -#define EMC_REQ_CTRL_0 MAKE_EMC_REG(0x2B0) - -#define EMC_EMC_STATUS_0 MAKE_EMC_REG(0x2B4) -#define EMC0_EMC_STATUS_0 MAKE_EMC0_REG(0x2B4) -#define EMC1_EMC_STATUS_0 MAKE_EMC1_REG(0x2B4) - -#define EMC_CFG_DIG_DLL_0 MAKE_EMC_REG(0x2BC) -#define EMC0_CFG_DIG_DLL_0 MAKE_EMC0_REG(0x2BC) -#define EMC1_CFG_DIG_DLL_0 MAKE_EMC1_REG(0x2BC) - -#define EMC_ZCAL_INTERVAL_0 MAKE_EMC_REG(0x2E0) - -#define EMC_PMC_SCRATCH3_0 MAKE_EMC_REG(0x448) - -#define EMC_FBIO_CFG7_0 MAKE_EMC_REG(0x584) - -#define EMC_PMACRO_CFG_PM_GLOBAL_0_0 MAKE_EMC_REG(0xC30) -#define EMC_PMACRO_TRAINING_CTRL_0_0 MAKE_EMC_REG(0xCF8) -#define EMC_PMACRO_TRAINING_CTRL_1_0 MAKE_EMC_REG(0xCFC) - -void emc_configure_pmacro_training(void); - -#endif diff --git a/exosphere/lp0fw/src/flow.c b/exosphere/lp0fw/src/flow.c deleted file mode 100644 index 9066ae9cb..000000000 --- a/exosphere/lp0fw/src/flow.c +++ /dev/null @@ -1,31 +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 "flow.h" - -void flow_perform_ram_repair(void) { - /* Perform repair only if not active cluster. */ - if (!(FLOW_CTLR_BPMP_CLUSTER_CONTROL_0 & 1)) { - /* Set REQ, to begin RAM repair. */ - FLOW_CTLR_RAM_REPAIR_0 = 1; - - /* Wait for STS to say RAM repair has completed. */ - while (!(FLOW_CTLR_RAM_REPAIR_0 & 2)) { } - } -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/flow.h b/exosphere/lp0fw/src/flow.h deleted file mode 100644 index bb27ad4c2..000000000 --- a/exosphere/lp0fw/src/flow.h +++ /dev/null @@ -1,35 +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_FLOW_CTLR_H -#define EXOSPHERE_WARMBOOT_BIN_FLOW_CTLR_H - -#include <stdint.h> -#include <stdbool.h> - -#include "utils.h" - -#define FLOW_BASE (0x60007000) - -#define MAKE_FLOW_REG(ofs) MAKE_REG32(FLOW_BASE + ofs) - -#define FLOW_CTLR_HALT_COP_EVENTS_0 MAKE_FLOW_REG(0x004) -#define FLOW_CTLR_RAM_REPAIR_0 MAKE_FLOW_REG(0x040) -#define FLOW_CTLR_BPMP_CLUSTER_CONTROL_0 MAKE_FLOW_REG(0x098) - -void flow_perform_ram_repair(void); - -#endif diff --git a/exosphere/lp0fw/src/fuse.c b/exosphere/lp0fw/src/fuse.c deleted file mode 100644 index 5ddf7e00d..000000000 --- a/exosphere/lp0fw/src/fuse.c +++ /dev/null @@ -1,75 +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 "fuse.h" -#include "car.h" -#include "pmc.h" - -#define NUM_FUSE_BYPASS_ENTRIES 0 - -bool fuse_check_downgrade_status(void) { - /* We aren't going to implement anti-downgrade. */ - return false; -} - -void fuse_disable_programming(void) { - FUSE_REGS->FUSE_DISABLEREGPROGRAM = 1; -} - -static fuse_bypass_data_t g_fuse_bypass_entries[NUM_FUSE_BYPASS_ENTRIES] = { - /* No entries here. */ -}; - -void fuse_configure_fuse_bypass(void) { - /* Make all fuse registers visible. */ - clkrst_enable_fuse_regs(true); - - /* Configure bypass/override, only if programming is allowed. */ - if (!(FUSE_REGS->FUSE_DISABLEREGPROGRAM & 1)) { - /* Enable write access and flush status. */ - FUSE_REGS->FUSE_WRITE_ACCESS_SW = (FUSE_REGS->FUSE_WRITE_ACCESS_SW & ~0x1) | 0x10000; - - /* Enable fuse bypass config. */ - FUSE_REGS->FUSE_FUSEBYPASS = 1; - - /* Override fuses. */ - for (size_t i = 0; i < NUM_FUSE_BYPASS_ENTRIES; i++) { - MAKE_FUSE_REG(g_fuse_bypass_entries[i].offset) = g_fuse_bypass_entries[i].value; - } - - /* Disable fuse write access. */ - FUSE_REGS->FUSE_WRITE_ACCESS_SW |= 1; - - /* Enable fuse bypass config. */ - /* I think this is a bug, and Nintendo meant to write 0 here? */ - FUSE_REGS->FUSE_FUSEBYPASS = 1; - - /* This...clears the disable programming bit(?). */ - /* I have no idea why this happens. What? */ - /* This is probably also either a bug or does nothing. */ - /* Is this bit even clearable? */ - FUSE_REGS->FUSE_DISABLEREGPROGRAM &= 0xFFFFFFFE; - - /* Restore saved private key disable bit. */ - FUSE_REGS->FUSE_PRIVATEKEYDISABLE |= (APBDEV_PMC_SECURE_SCRATCH21_0 & 0x10); - - /* Lock private key disable secure scratch. */ - APBDEV_PMC_SEC_DISABLE2_0 |= 0x4000000; - } -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/fuse.h b/exosphere/lp0fw/src/fuse.h deleted file mode 100644 index ae6d350e9..000000000 --- a/exosphere/lp0fw/src/fuse.h +++ /dev/null @@ -1,213 +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_FUSE_H -#define EXOSPHERE_WARMBOOT_BIN_FUSE_H - -#include <stdbool.h> -#include <stdint.h> - -#include "utils.h" - -typedef struct { - uint32_t FUSE_FUSECTRL; - uint32_t FUSE_FUSEADDR; - uint32_t FUSE_FUSERDATA; - uint32_t FUSE_FUSEWDATA; - uint32_t FUSE_FUSETIME_RD1; - uint32_t FUSE_FUSETIME_RD2; - uint32_t FUSE_FUSETIME_PGM1; - uint32_t FUSE_FUSETIME_PGM2; - uint32_t FUSE_PRIV2INTFC_START; - uint32_t FUSE_FUSEBYPASS; - uint32_t FUSE_PRIVATEKEYDISABLE; - uint32_t FUSE_DISABLEREGPROGRAM; - uint32_t FUSE_WRITE_ACCESS_SW; - uint32_t FUSE_PWR_GOOD_SW; - uint32_t _0x38; - uint32_t FUSE_PRIV2RESHIFT; - uint32_t _0x40[0x3]; - uint32_t FUSE_FUSETIME_RD3; - uint32_t _0x50[0xC]; - uint32_t FUSE_PRIVATE_KEY0_NONZERO; - uint32_t FUSE_PRIVATE_KEY1_NONZERO; - uint32_t FUSE_PRIVATE_KEY2_NONZERO; - uint32_t FUSE_PRIVATE_KEY3_NONZERO; - uint32_t FUSE_PRIVATE_KEY4_NONZERO; - uint32_t _0x90[0x1C]; -} tegra_fuse_t; - -typedef struct { - uint32_t FUSE_PRODUCTION_MODE; - uint32_t FUSE_JTAG_SECUREID_VALID; - uint32_t FUSE_ODM_LOCK; - uint32_t FUSE_OPT_OPENGL_EN; - uint32_t FUSE_SKU_INFO; - uint32_t FUSE_CPU_SPEEDO_0_CALIB; - uint32_t FUSE_CPU_IDDQ_CALIB; - uint32_t FUSE_DAC_CRT_CALIB; - uint32_t FUSE_DAC_HDTV_CALIB; - uint32_t FUSE_DAC_SDTV_CALIB; - uint32_t FUSE_OPT_FT_REV; - uint32_t FUSE_CPU_SPEEDO_1_CALIB; - uint32_t FUSE_CPU_SPEEDO_2_CALIB; - uint32_t FUSE_SOC_SPEEDO_0_CALIB; - uint32_t FUSE_SOC_SPEEDO_1_CALIB; - uint32_t FUSE_SOC_SPEEDO_2_CALIB; - uint32_t FUSE_SOC_IDDQ_CALIB; - uint32_t FUSE_RESERVED_PRODUCTION_WP; - uint32_t FUSE_FA; - uint32_t FUSE_RESERVED_PRODUCTION; - uint32_t FUSE_HDMI_LANE0_CALIB; - uint32_t FUSE_HDMI_LANE1_CALIB; - uint32_t FUSE_HDMI_LANE2_CALIB; - uint32_t FUSE_HDMI_LANE3_CALIB; - uint32_t FUSE_ENCRYPTION_RATE; - uint32_t FUSE_PUBLIC_KEY[0x8]; - uint32_t FUSE_TSENSOR1_CALIB; - uint32_t FUSE_TSENSOR2_CALIB; - uint32_t FUSE_VSENSOR_CALIB; - uint32_t FUSE_OPT_CP_REV; - uint32_t FUSE_OPT_PFG; - uint32_t FUSE_TSENSOR0_CALIB; - uint32_t FUSE_FIRST_BOOTROM_PATCH_SIZE; - uint32_t FUSE_SECURITY_MODE; - uint32_t FUSE_PRIVATE_KEY[0x5]; - uint32_t FUSE_ARM_JTAG_DIS; - uint32_t FUSE_BOOT_DEVICE_INFO; - uint32_t FUSE_RESERVED_SW; - uint32_t FUSE_OPT_VP9_DISABLE; - uint32_t FUSE_RESERVED_ODM[0x8]; - uint32_t FUSE_OBS_DIS; - uint32_t FUSE_NOR_INFO; - uint32_t FUSE_USB_CALIB; - uint32_t FUSE_SKU_DIRECT_CONFIG; - uint32_t FUSE_KFUSE_PRIVKEY_CTRL; - uint32_t FUSE_PACKAGE_INFO; - uint32_t FUSE_OPT_VENDOR_CODE; - uint32_t FUSE_OPT_FAB_CODE; - uint32_t FUSE_OPT_LOT_CODE_0; - uint32_t FUSE_OPT_LOT_CODE_1; - uint32_t FUSE_OPT_WAFER_ID; - uint32_t FUSE_OPT_X_COORDINATE; - uint32_t FUSE_OPT_Y_COORDINATE; - uint32_t FUSE_OPT_SEC_DEBUG_EN; - uint32_t FUSE_OPT_OPS_RESERVED; - uint32_t FUSE_SATA_CALIB; - uint32_t FUSE_GPU_IDDQ_CALIB; - uint32_t FUSE_TSENSOR3_CALIB; - uint32_t FUSE_SKU_BOND_OUT_L; - uint32_t FUSE_SKU_BOND_OUT_H; - uint32_t FUSE_SKU_BOND_OUT_U; - uint32_t FUSE_SKU_BOND_OUT_V; - uint32_t FUSE_SKU_BOND_OUT_W; - uint32_t FUSE_OPT_SAMPLE_TYPE; - uint32_t FUSE_OPT_SUBREVISION; - uint32_t FUSE_OPT_SW_RESERVED_0; - uint32_t FUSE_OPT_SW_RESERVED_1; - uint32_t FUSE_TSENSOR4_CALIB; - uint32_t FUSE_TSENSOR5_CALIB; - uint32_t FUSE_TSENSOR6_CALIB; - uint32_t FUSE_TSENSOR7_CALIB; - uint32_t FUSE_OPT_PRIV_SEC_EN; - uint32_t FUSE_PKC_DISABLE; - uint32_t _0x16C; - uint32_t _0x170; - uint32_t _0x174; - uint32_t _0x178; - uint32_t FUSE_FUSE2TSEC_DEBUG_DISABLE; - uint32_t FUSE_TSENSOR_COMMON; - uint32_t FUSE_OPT_CP_BIN; - uint32_t FUSE_OPT_GPU_DISABLE; - uint32_t FUSE_OPT_FT_BIN; - uint32_t FUSE_OPT_DONE_MAP; - uint32_t _0x194; - uint32_t FUSE_APB2JTAG_DISABLE; - uint32_t FUSE_ODM_INFO; - uint32_t _0x1A0; - uint32_t _0x1A4; - uint32_t FUSE_ARM_CRYPT_DE_FEATURE; - uint32_t _0x1AC; - uint32_t _0x1B0; - uint32_t _0x1B4; - uint32_t _0x1B8; - uint32_t _0x1BC; - uint32_t FUSE_WOA_SKU_FLAG; - uint32_t FUSE_ECO_RESERVE_1; - uint32_t FUSE_GCPLEX_CONFIG_FUSE; - uint32_t FUSE_PRODUCTION_MONTH; - uint32_t FUSE_RAM_REPAIR_INDICATOR; - uint32_t FUSE_TSENSOR9_CALIB; - uint32_t _0x1D8; - uint32_t FUSE_VMIN_CALIBRATION; - uint32_t FUSE_AGING_SENSOR_CALIBRATION; - uint32_t FUSE_DEBUG_AUTHENTICATION; - uint32_t FUSE_SECURE_PROVISION_INDEX; - uint32_t FUSE_SECURE_PROVISION_INFO; - uint32_t FUSE_OPT_GPU_DISABLE_CP1; - uint32_t FUSE_SPARE_ENDIS; - uint32_t FUSE_ECO_RESERVE_0; - uint32_t _0x1FC; - uint32_t _0x200; - uint32_t FUSE_RESERVED_CALIB0; - uint32_t FUSE_RESERVED_CALIB1; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE_CP1; - uint32_t FUSE_OPT_CPU_DISABLE; - uint32_t FUSE_OPT_CPU_DISABLE_CP1; - uint32_t FUSE_TSENSOR10_CALIB; - uint32_t FUSE_TSENSOR10_CALIB_AUX; - uint32_t FUSE_OPT_RAM_SVOP_DP; - uint32_t FUSE_OPT_RAM_SVOP_PDP; - uint32_t FUSE_OPT_RAM_SVOP_REG; - uint32_t FUSE_OPT_RAM_SVOP_SP; - uint32_t FUSE_OPT_RAM_SVOP_SMPDP; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE_CP2; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE_CP1; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE_CP2; - uint32_t FUSE_OPT_CPU_DISABLE_CP2; - uint32_t FUSE_OPT_GPU_DISABLE_CP2; - uint32_t FUSE_USB_CALIB_EXT; - uint32_t FUSE_RESERVED_FIELD; - uint32_t FUSE_OPT_ECC_EN; - uint32_t _0x25C; - uint32_t _0x260; - uint32_t _0x264; - uint32_t _0x268; - uint32_t _0x26C; - uint32_t _0x270; - uint32_t _0x274; - uint32_t _0x278; - uint32_t FUSE_SPARE_REALIGNMENT_REG; - uint32_t FUSE_SPARE_BIT[0x20]; -} tegra_fuse_chip_t; - -#define FUSE_REGS ((volatile tegra_fuse_t *)(0x7000F800)) -#define FUSE_CHIP_REGS ((volatile tegra_fuse_chip_t *)(0x7000F900)) - -#define MAKE_FUSE_REG(n) MAKE_REG32(0x7000F800 + n) - -typedef struct { - uint32_t offset; - uint32_t value; -} fuse_bypass_data_t; - -bool fuse_check_downgrade_status(void); -void fuse_configure_fuse_bypass(void); -void fuse_disable_programming(void); - -#endif diff --git a/exosphere/lp0fw/src/i2c.c b/exosphere/lp0fw/src/i2c.c deleted file mode 100644 index 75d96df3b..000000000 --- a/exosphere/lp0fw/src/i2c.c +++ /dev/null @@ -1,76 +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 "i2c.h" -#include "timer.h" - -/* Prototypes for internal commands. */ -void i2c_set_test_master_config_load(void); -void i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes); -void i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b); - -/* Load hardware config for I2C5. */ -void i2c_set_test_master_config_load(void) { - /* Set MSTR_CONFIG_LOAD. */ - I2C_I2C_CONFIG_LOAD_0 = 0x1; - - while (I2C_I2C_CONFIG_LOAD_0 & 1) { - /* Wait forever until it's unset. */ - } -} - -/* Writes a value to an i2c device. */ -void i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes) { - if (num_bytes > 4) { - return; - } - - /* Set device for 7-bit mode. */ - I2C_I2C_CMD_ADDR0_0 = device << 1; - - /* Load in data to write. */ - I2C_I2C_CMD_DATA1_0 = val; - - /* Set config with LENGTH = num_bytes, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2800; - - i2c_set_test_master_config_load(); - - /* Config |= SEND; */ - I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2800 | 0x200; - - - while (I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - while (I2C_I2C_STATUS_0 & 0xF) { - /* Wait until write successful. */ - } -} - -/* Writes a byte val to reg for given device. */ -void i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b) { - uint32_t val = (reg) | (b << 8); - /* Write 1 byte (reg) + 1 byte (value) */ - i2c_write(device, val, 2); -} - -/* Enable the PMIC. */ -void i2c_enable_pmic(void) { - /* Write 00 to Device 27 Reg 00. */ - i2c_send_byte_command(27, 0, 0x80); -} diff --git a/exosphere/lp0fw/src/i2c.h b/exosphere/lp0fw/src/i2c.h deleted file mode 100644 index 672bb2de6..000000000 --- a/exosphere/lp0fw/src/i2c.h +++ /dev/null @@ -1,48 +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_I2C_H -#define EXOSPHERE_WARMBOOT_BIN_I2C_H - -#include "utils.h" - -/* I2C_BASE = I2C5. */ -#define I2C_BASE (0x7000D000) - -#define MAKE_I2C_REG(ofs) (MAKE_REG32(I2C_BASE + ofs)) - -#define I2C_I2C_CNFG_0 MAKE_I2C_REG(0x000) - -#define I2C_I2C_CMD_ADDR0_0 MAKE_I2C_REG(0x004) - -#define I2C_I2C_CMD_DATA1_0 MAKE_I2C_REG(0x00C) - -#define I2C_I2C_STATUS_0 MAKE_I2C_REG(0x01C) - -#define I2C_INTERRUPT_STATUS_REGISTER_0 MAKE_I2C_REG(0x068) - -#define I2C_I2C_CLK_DIVISOR_REGISTER_0 MAKE_I2C_REG(0x06C) - -#define I2C_I2C_BUS_CLEAR_CONFIG_0 MAKE_I2C_REG(0x084) - -#define I2C_I2C_BUS_CLEAR_STATUS_0 MAKE_I2C_REG(0x088) - - -#define I2C_I2C_CONFIG_LOAD_0 MAKE_I2C_REG(0x08C) - -void i2c_enable_pmic(void); - -#endif diff --git a/exosphere/lp0fw/src/lp0.c b/exosphere/lp0fw/src/lp0.c deleted file mode 100644 index 5bd0b8a99..000000000 --- a/exosphere/lp0fw/src/lp0.c +++ /dev/null @@ -1,98 +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 "utils.h" -#include "lp0.h" -#include "mc.h" -#include "pmc.h" -#include "misc.h" -#include "fuse.h" -#include "car.h" -#include "emc.h" -#include "cluster.h" -#include "flow.h" -#include "timer.h" -#include "secmon.h" - -void reboot(void) { - /* Write MAIN_RST */ - APBDEV_PMC_CNTRL_0 = 0x10; - while (true) { - /* Wait for reboot. */ - } -} - -void lp0_entry_main(warmboot_metadata_t *meta) { - const uint32_t target_firmware = meta->target_firmware; - /* Before doing anything else, ensure some sanity. */ - if (meta->magic != WARMBOOT_MAGIC || target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX) { - reboot(); - } - - /* [4.0.0+] First thing warmboot does is disable BPMP access to memory. */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - disable_bpmp_access_to_dram(); - } - - /* Configure debugging depending on FUSE_PRODUCTION_MODE */ - misc_configure_device_dbg_settings(); - - /* Check for downgrade. */ - /* NOTE: We implemented this as "return false" */ - if (fuse_check_downgrade_status()) { - reboot(); - } - - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - /* Nintendo's firmware checks APBDEV_PMC_SECURE_SCRATCH32_0 against a per-warmboot binary value here. */ - /* We won't bother with that. */ - if (false /* APBDEV_PMC_SECURE_SCRATCH32_0 == WARMBOOT_MAGIC_NUMBER */) { - reboot(); - } - } - - /* TODO: Check that we're running at the correct physical address. */ - - /* Setup fuses, disable bypass. */ - fuse_configure_fuse_bypass(); - - /* Configure oscillators/timing in CAR. */ - car_configure_oscillators(); - - /* Restore RAM configuration. */ - misc_restore_ram_svop(); - emc_configure_pmacro_training(); - - /* Setup clock output for all devices, working around mbist bug. */ - car_mbist_workaround(); - - /* Initialize the CPU cluster. */ - cluster_initialize_cpu(); - - secmon_restore_to_tzram(target_firmware); - - /* Power on the CPU cluster. */ - cluster_power_on_cpu(); - - /* Nintendo clears most of warmboot.bin out of IRAM here. We're not gonna bother. */ - /* memset( ... ); */ - - const uint32_t halt_val = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? 0x40000000 : 0x50000000; - while (true) { - /* Halt the BPMP. */ - FLOW_CTLR_HALT_COP_EVENTS_0 = halt_val; - } -} diff --git a/exosphere/lp0fw/src/lp0.h b/exosphere/lp0fw/src/lp0.h deleted file mode 100644 index ca5679649..000000000 --- a/exosphere/lp0fw/src/lp0.h +++ /dev/null @@ -1,35 +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_LP0_H -#define EXOSPHERE_WARMBOOT_BIN_LP0_H - -#include "utils.h" - -/* WBT0 */ -#define WARMBOOT_MAGIC 0x30544257 - -typedef struct { - uint32_t magic; - uint32_t target_firmware; - uint32_t padding[2]; -} warmboot_metadata_t; - -void lp0_entry_main(warmboot_metadata_t *meta); - -void __attribute__((noreturn)) reboot(void); - -#endif diff --git a/exosphere/lp0fw/src/mc.c b/exosphere/lp0fw/src/mc.c deleted file mode 100644 index 522b1f475..000000000 --- a/exosphere/lp0fw/src/mc.c +++ /dev/null @@ -1,40 +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 "mc.h" -#include "utils.h" - -void disable_bpmp_access_to_dram(void) { - /* Modify carveout 4 to prevent BPMP access to dram (TZ will fix it). */ - volatile security_carveout_t *carveout = (volatile security_carveout_t *)(MC_BASE + 0xC08 + 0x50 * (4 - CARVEOUT_ID_MIN)); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 1; /* 128 KiB */ - carveout->client_access_0 = 0; - carveout->client_access_1 = 0; - carveout->client_access_2 = 0; - carveout->client_access_3 = 0; - carveout->client_access_4 = 0; - carveout->client_force_internal_access_0 = BIT(CSR_AVPCARM7R); - carveout->client_force_internal_access_1 = BIT(CSW_AVPCARM7W); - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - /* Set config to LOCKED, TZ-SECURE, untranslated addresses only. */ - carveout->config = 0x8F; -} diff --git a/exosphere/lp0fw/src/mc.h b/exosphere/lp0fw/src/mc.h deleted file mode 100644 index 5e73730ce..000000000 --- a/exosphere/lp0fw/src/mc.h +++ /dev/null @@ -1,622 +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_MC_H -#define EXOSPHERE_WARMBOOT_BIN_MC_H - -#include <stdint.h> - -#define MC_BASE_PHYS 0x70019000 - -#define MC_BASE (MC_BASE_PHYS) -#define MAKE_MC_REG(n) MAKE_REG32(MC_BASE + n) - -#define MC_INTSTATUS 0x0 -#define MC_INTMASK 0x4 -#define MC_ERR_STATUS 0x8 -#define MC_ERR_ADR 0xc -#define MC_SMMU_CONFIG 0x10 -#define MC_SMMU_TLB_CONFIG 0x14 -#define MC_SMMU_PTC_CONFIG 0x18 -#define MC_SMMU_PTB_ASID 0x1c -#define MC_SMMU_PTB_DATA 0x20 -#define MC_SMMU_TLB_FLUSH 0x30 -#define MC_SMMU_PTC_FLUSH 0x34 -#define MC_SMMU_ASID_SECURITY 0x38 -#define MC_SMMU_ASID_SECURITY_1 0x3c -#define MC_SMMU_ASID_SECURITY_2 0x9e0 -#define MC_SMMU_ASID_SECURITY_3 0x9e4 -#define MC_SMMU_ASID_SECURITY_4 0x9e8 -#define MC_SMMU_ASID_SECURITY_5 0x9ec -#define MC_SMMU_ASID_SECURITY_6 0x9f0 -#define MC_SMMU_ASID_SECURITY_7 0x9f4 -#define MC_SMMU_AFI_ASID 0x238 -#define MC_SMMU_AVPC_ASID 0x23c -#define MC_SMMU_PPCS1_ASID 0x298 -#define MC_SMMU_TRANSLATION_ENABLE_0 0x228 -#define MC_SMMU_TRANSLATION_ENABLE_1 0x22c -#define MC_SMMU_TRANSLATION_ENABLE_2 0x230 -#define MC_SMMU_TRANSLATION_ENABLE_3 0x234 -#define MC_SMMU_TRANSLATION_ENABLE_4 0xb98 -#define MC_PCFIFO_CLIENT_CONFIG0 0xdd0 -#define MC_PCFIFO_CLIENT_CONFIG1 0xdd4 -#define MC_PCFIFO_CLIENT_CONFIG2 0xdd8 -#define MC_PCFIFO_CLIENT_CONFIG3 0xddc -#define MC_PCFIFO_CLIENT_CONFIG4 0xde0 -#define MC_EMEM_CFG 0x50 -#define MC_EMEM_ADR_CFG 0x54 -#define MC_EMEM_ADR_CFG_DEV0 0x58 -#define MC_EMEM_ADR_CFG_DEV1 0x5c -#define MC_EMEM_ADR_CFG_CHANNEL_MASK 0x60 -#define MC_EMEM_ADR_CFG_BANK_MASK_0 0x64 -#define MC_EMEM_ADR_CFG_BANK_MASK_1 0x68 -#define MC_EMEM_ADR_CFG_BANK_MASK_2 0x6c -#define MC_SECURITY_CFG0 0x70 -#define MC_SECURITY_CFG1 0x74 -#define MC_SECURITY_CFG3 0x9bc -#define MC_SECURITY_RSV 0x7c -#define MC_EMEM_ARB_CFG 0x90 -#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 -#define MC_EMEM_ARB_TIMING_RCD 0x98 -#define MC_EMEM_ARB_TIMING_RP 0x9c -#define MC_EMEM_ARB_TIMING_RC 0xa0 -#define MC_EMEM_ARB_TIMING_RAS 0xa4 -#define MC_EMEM_ARB_TIMING_FAW 0xa8 -#define MC_EMEM_ARB_TIMING_RRD 0xac -#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 -#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 -#define MC_EMEM_ARB_TIMING_R2R 0xb8 -#define MC_EMEM_ARB_TIMING_W2W 0xbc -#define MC_EMEM_ARB_TIMING_R2W 0xc0 -#define MC_EMEM_ARB_TIMING_W2R 0xc4 -#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0 -#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4 -#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0 -#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4 -#define MC_EMEM_ARB_DA_TURNS 0xd0 -#define MC_EMEM_ARB_DA_COVERS 0xd4 -#define MC_EMEM_ARB_MISC0 0xd8 -#define MC_EMEM_ARB_MISC1 0xdc -#define MC_EMEM_ARB_MISC2 0xc8 -#define MC_EMEM_ARB_RING1_THROTTLE 0xe0 -#define MC_EMEM_ARB_RING3_THROTTLE 0xe4 -#define MC_EMEM_ARB_NISO_THROTTLE 0x6b0 -#define MC_EMEM_ARB_OVERRIDE 0xe8 -#define MC_EMEM_ARB_RSV 0xec -#define MC_CLKEN_OVERRIDE 0xf4 -#define MC_TIMING_CONTROL_DBG 0xf8 -#define MC_TIMING_CONTROL 0xfc -#define MC_STAT_CONTROL 0x100 -#define MC_STAT_STATUS 0x104 -#define MC_STAT_EMC_CLOCK_LIMIT 0x108 -#define MC_STAT_EMC_CLOCK_LIMIT_MSBS 0x10c -#define MC_STAT_EMC_CLOCKS 0x110 -#define MC_STAT_EMC_CLOCKS_MSBS 0x114 -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO 0x118 -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO 0x158 -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI 0x11c -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI 0x15c -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER 0xa20 -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER 0xa24 -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_LO 0x198 -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_LO 0x1a8 -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_HI 0x19c -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_HI 0x1ac -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_UPPER 0xa28 -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_UPPER 0xa2c -#define MC_STAT_EMC_FILTER_SET0_ASID 0x1a0 -#define MC_STAT_EMC_FILTER_SET1_ASID 0x1b0 -#define MC_STAT_EMC_FILTER_SET0_SLACK_LIMIT 0x120 -#define MC_STAT_EMC_FILTER_SET1_SLACK_LIMIT 0x160 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 0x128 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 0x168 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 0x12c -#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 0x16c -#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 0x130 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 0x170 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 0x134 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 0xb88 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 0x174 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 0xb8c -#define MC_STAT_EMC_SET0_COUNT 0x138 -#define MC_STAT_EMC_SET0_COUNT_MSBS 0x13c -#define MC_STAT_EMC_SET1_COUNT 0x178 -#define MC_STAT_EMC_SET1_COUNT_MSBS 0x17c -#define MC_STAT_EMC_SET0_SLACK_ACCUM 0x140 -#define MC_STAT_EMC_SET0_SLACK_ACCUM_MSBS 0x144 -#define MC_STAT_EMC_SET1_SLACK_ACCUM 0x180 -#define MC_STAT_EMC_SET1_SLACK_ACCUM_MSBS 0x184 -#define MC_STAT_EMC_SET0_HISTO_COUNT 0x148 -#define MC_STAT_EMC_SET0_HISTO_COUNT_MSBS 0x14c -#define MC_STAT_EMC_SET1_HISTO_COUNT 0x188 -#define MC_STAT_EMC_SET1_HISTO_COUNT_MSBS 0x18c -#define MC_STAT_EMC_SET0_MINIMUM_SLACK_OBSERVED 0x150 -#define MC_STAT_EMC_SET1_MINIMUM_SLACK_OBSERVED 0x190 -#define MC_STAT_EMC_SET0_IDLE_CYCLE_COUNT 0x1b8 -#define MC_STAT_EMC_SET0_IDLE_CYCL_COUNT_MSBS 0x1bc -#define MC_STAT_EMC_SET1_IDLE_CYCLE_COUNT 0x1c8 -#define MC_STAT_EMC_SET1_IDLE_CYCL_COUNT_MSBS 0x1cc -#define MC_STAT_EMC_SET0_IDLE_CYCLE_PARTITION_SELECT 0x1c0 -#define MC_STAT_EMC_SET1_IDLE_CYCLE_PARTITION_SELECT 0x1d0 -#define MC_CLIENT_HOTRESET_CTRL 0x200 -#define MC_CLIENT_HOTRESET_CTRL_1 0x970 -#define MC_CLIENT_HOTRESET_STATUS 0x204 -#define MC_CLIENT_HOTRESET_STATUS_1 0x974 -#define MC_EMEM_ARB_ISOCHRONOUS_0 0x208 -#define MC_EMEM_ARB_ISOCHRONOUS_1 0x20c -#define MC_EMEM_ARB_ISOCHRONOUS_2 0x210 -#define MC_EMEM_ARB_ISOCHRONOUS_3 0x214 -#define MC_EMEM_ARB_ISOCHRONOUS_4 0xb94 -#define MC_EMEM_ARB_HYSTERESIS_0 0x218 -#define MC_EMEM_ARB_HYSTERESIS_1 0x21c -#define MC_EMEM_ARB_HYSTERESIS_2 0x220 -#define MC_EMEM_ARB_HYSTERESIS_3 0x224 -#define MC_EMEM_ARB_HYSTERESIS_4 0xb84 -#define MC_EMEM_ARB_DHYSTERESIS_0 0xbb0 -#define MC_EMEM_ARB_DHYSTERESIS_1 0xbb4 -#define MC_EMEM_ARB_DHYSTERESIS_2 0xbb8 -#define MC_EMEM_ARB_DHYSTERESIS_3 0xbbc -#define MC_EMEM_ARB_DHYSTERESIS_4 0xbc0 -#define MC_EMEM_ARB_DHYST_CTRL 0xbcc -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec -#define MC_RESERVED_RSV 0x3fc -#define MC_DISB_EXTRA_SNAP_LEVELS 0x408 -#define MC_APB_EXTRA_SNAP_LEVELS 0x2a4 -#define MC_AHB_EXTRA_SNAP_LEVELS 0x2a0 -#define MC_USBD_EXTRA_SNAP_LEVELS 0xa18 -#define MC_ISP_EXTRA_SNAP_LEVELS 0xa08 -#define MC_AUD_EXTRA_SNAP_LEVELS 0xa10 -#define MC_MSE_EXTRA_SNAP_LEVELS 0x40c -#define MC_GK2_EXTRA_SNAP_LEVELS 0xa40 -#define MC_A9AVPPC_EXTRA_SNAP_LEVELS 0x414 -#define MC_FTOP_EXTRA_SNAP_LEVELS 0x2bc -#define MC_JPG_EXTRA_SNAP_LEVELS 0xa3c -#define MC_HOST_EXTRA_SNAP_LEVELS 0xa14 -#define MC_SAX_EXTRA_SNAP_LEVELS 0x2c0 -#define MC_DIS_EXTRA_SNAP_LEVELS 0x2ac -#define MC_VICPC_EXTRA_SNAP_LEVELS 0xa1c -#define MC_HDAPC_EXTRA_SNAP_LEVELS 0xa48 -#define MC_AVP_EXTRA_SNAP_LEVELS 0x2a8 -#define MC_USBX_EXTRA_SNAP_LEVELS 0x404 -#define MC_PCX_EXTRA_SNAP_LEVELS 0x2b8 -#define MC_SD_EXTRA_SNAP_LEVELS 0xa04 -#define MC_DFD_EXTRA_SNAP_LEVELS 0xa4c -#define MC_VE_EXTRA_SNAP_LEVELS 0x2d8 -#define MC_GK_EXTRA_SNAP_LEVELS 0xa00 -#define MC_VE2_EXTRA_SNAP_LEVELS 0x410 -#define MC_SDM_EXTRA_SNAP_LEVELS 0xa44 -#define MC_VIDEO_PROTECT_BOM 0x648 -#define MC_VIDEO_PROTECT_SIZE_MB 0x64c -#define MC_VIDEO_PROTECT_BOM_ADR_HI 0x978 -#define MC_VIDEO_PROTECT_REG_CTRL 0x650 -#define MC_ERR_VPR_STATUS 0x654 -#define MC_ERR_VPR_ADR 0x658 -#define MC_VIDEO_PROTECT_VPR_OVERRIDE 0x418 -#define MC_VIDEO_PROTECT_VPR_OVERRIDE1 0x590 -#define MC_IRAM_BOM 0x65c -#define MC_IRAM_TOM 0x660 -#define MC_IRAM_ADR_HI 0x980 -#define MC_IRAM_REG_CTRL 0x964 -#define MC_EMEM_CFG_ACCESS_CTRL 0x664 -#define MC_TZ_SECURITY_CTRL 0x668 -#define MC_EMEM_ARB_OUTSTANDING_REQ_RING3 0x66c -#define MC_EMEM_ARB_OUTSTANDING_REQ_NISO 0x6b4 -#define MC_EMEM_ARB_RING0_THROTTLE_MASK 0x6bc -#define MC_EMEM_ARB_NISO_THROTTLE_MASK 0x6b8 -#define MC_EMEM_ARB_NISO_THROTTLE_MASK_1 0xb80 -#define MC_SEC_CARVEOUT_BOM 0x670 -#define MC_SEC_CARVEOUT_SIZE_MB 0x674 -#define MC_SEC_CARVEOUT_ADR_HI 0x9d4 -#define MC_SEC_CARVEOUT_REG_CTRL 0x678 -#define MC_ERR_SEC_STATUS 0x67c -#define MC_ERR_SEC_ADR 0x680 -#define MC_PC_IDLE_CLOCK_GATE_CONFIG 0x684 -#define MC_STUTTER_CONTROL 0x688 -#define MC_RESERVED_RSV_1 0x958 -#define MC_DVFS_PIPE_SELECT 0x95c -#define MC_AHB_PTSA_MIN 0x4e0 -#define MC_AUD_PTSA_MIN 0x54c -#define MC_MLL_MPCORER_PTSA_RATE 0x44c -#define MC_RING2_PTSA_RATE 0x440 -#define MC_USBD_PTSA_RATE 0x530 -#define MC_USBX_PTSA_MIN 0x528 -#define MC_USBD_PTSA_MIN 0x534 -#define MC_APB_PTSA_MAX 0x4f0 -#define MC_JPG_PTSA_RATE 0x584 -#define MC_DIS_PTSA_MIN 0x420 -#define MC_AVP_PTSA_MAX 0x4fc -#define MC_AVP_PTSA_RATE 0x4f4 -#define MC_RING1_PTSA_MIN 0x480 -#define MC_DIS_PTSA_MAX 0x424 -#define MC_SD_PTSA_MAX 0x4d8 -#define MC_MSE_PTSA_RATE 0x4c4 -#define MC_VICPC_PTSA_MIN 0x558 -#define MC_PCX_PTSA_MAX 0x4b4 -#define MC_ISP_PTSA_RATE 0x4a0 -#define MC_A9AVPPC_PTSA_MIN 0x48c -#define MC_RING2_PTSA_MAX 0x448 -#define MC_AUD_PTSA_RATE 0x548 -#define MC_HOST_PTSA_MIN 0x51c -#define MC_MLL_MPCORER_PTSA_MAX 0x454 -#define MC_SD_PTSA_MIN 0x4d4 -#define MC_RING1_PTSA_RATE 0x47c -#define MC_JPG_PTSA_MIN 0x588 -#define MC_HDAPC_PTSA_MIN 0x62c -#define MC_AVP_PTSA_MIN 0x4f8 -#define MC_JPG_PTSA_MAX 0x58c -#define MC_VE_PTSA_MAX 0x43c -#define MC_DFD_PTSA_MAX 0x63c -#define MC_VICPC_PTSA_RATE 0x554 -#define MC_GK_PTSA_MAX 0x544 -#define MC_VICPC_PTSA_MAX 0x55c -#define MC_SDM_PTSA_MAX 0x624 -#define MC_SAX_PTSA_RATE 0x4b8 -#define MC_PCX_PTSA_MIN 0x4b0 -#define MC_APB_PTSA_MIN 0x4ec -#define MC_GK2_PTSA_MIN 0x614 -#define MC_PCX_PTSA_RATE 0x4ac -#define MC_RING1_PTSA_MAX 0x484 -#define MC_HDAPC_PTSA_RATE 0x628 -#define MC_MLL_MPCORER_PTSA_MIN 0x450 -#define MC_GK2_PTSA_MAX 0x618 -#define MC_AUD_PTSA_MAX 0x550 -#define MC_GK2_PTSA_RATE 0x610 -#define MC_ISP_PTSA_MAX 0x4a8 -#define MC_DISB_PTSA_RATE 0x428 -#define MC_VE2_PTSA_MAX 0x49c -#define MC_DFD_PTSA_MIN 0x638 -#define MC_FTOP_PTSA_RATE 0x50c -#define MC_A9AVPPC_PTSA_RATE 0x488 -#define MC_VE2_PTSA_MIN 0x498 -#define MC_USBX_PTSA_MAX 0x52c -#define MC_DIS_PTSA_RATE 0x41c -#define MC_USBD_PTSA_MAX 0x538 -#define MC_A9AVPPC_PTSA_MAX 0x490 -#define MC_USBX_PTSA_RATE 0x524 -#define MC_FTOP_PTSA_MAX 0x514 -#define MC_HDAPC_PTSA_MAX 0x630 -#define MC_SD_PTSA_RATE 0x4d0 -#define MC_DFD_PTSA_RATE 0x634 -#define MC_FTOP_PTSA_MIN 0x510 -#define MC_SDM_PTSA_RATE 0x61c -#define MC_AHB_PTSA_RATE 0x4dc -#define MC_SMMU_SMMU_PTSA_MAX 0x460 -#define MC_RING2_PTSA_MIN 0x444 -#define MC_SDM_PTSA_MIN 0x620 -#define MC_APB_PTSA_RATE 0x4e8 -#define MC_MSE_PTSA_MIN 0x4c8 -#define MC_HOST_PTSA_RATE 0x518 -#define MC_VE_PTSA_RATE 0x434 -#define MC_AHB_PTSA_MAX 0x4e4 -#define MC_SAX_PTSA_MIN 0x4bc -#define MC_SMMU_SMMU_PTSA_MIN 0x45c -#define MC_ISP_PTSA_MIN 0x4a4 -#define MC_HOST_PTSA_MAX 0x520 -#define MC_SAX_PTSA_MAX 0x4c0 -#define MC_VE_PTSA_MIN 0x438 -#define MC_GK_PTSA_MIN 0x540 -#define MC_MSE_PTSA_MAX 0x4cc -#define MC_DISB_PTSA_MAX 0x430 -#define MC_DISB_PTSA_MIN 0x42c -#define MC_SMMU_SMMU_PTSA_RATE 0x458 -#define MC_VE2_PTSA_RATE 0x494 -#define MC_GK_PTSA_RATE 0x53c -#define MC_PTSA_GRANT_DECREMENT 0x960 -#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4 -#define MC_LATENCY_ALLOWANCE_AXIAP_0 0x3a0 -#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380 -#define MC_LATENCY_ALLOWANCE_ISP2B_0 0x384 -#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc -#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8 -#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370 -#define MC_LATENCY_ALLOWANCE_SE_0 0x3e0 -#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374 -#define MC_LATENCY_ALLOWANCE_DC_0 0x2e8 -#define MC_LATENCY_ALLOWANCE_VIC_0 0x394 -#define MC_LATENCY_ALLOWANCE_DCB_1 0x2f8 -#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8 -#define MC_LATENCY_ALLOWANCE_DCB_2 0x2fc -#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390 -#define MC_LATENCY_ALLOWANCE_DC_2 0x2f0 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB 0x694 -#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348 -#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c -#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344 -#define MC_LATENCY_ALLOWANCE_TSECB_0 0x3f0 -#define MC_LATENCY_ALLOWANCE_AFI_0 0x2e0 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B 0x698 -#define MC_LATENCY_ALLOWANCE_DC_1 0x2ec -#define MC_LATENCY_ALLOWANCE_APE_0 0x3dc -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C 0x6a0 -#define MC_LATENCY_ALLOWANCE_A9AVP_0 0x3a4 -#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8 -#define MC_LATENCY_ALLOWANCE_DCB_0 0x2f4 -#define MC_LATENCY_ALLOWANCE_HC_1 0x314 -#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0 -#define MC_LATENCY_ALLOWANCE_NVJPG_0 0x3e4 -#define MC_LATENCY_ALLOWANCE_PTC_0 0x34c -#define MC_LATENCY_ALLOWANCE_ETR_0 0x3ec -#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320 -#define MC_LATENCY_ALLOWANCE_VI2_0 0x398 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB 0x69c -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB 0x6a4 -#define MC_LATENCY_ALLOWANCE_SATA_0 0x350 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A 0x690 -#define MC_LATENCY_ALLOWANCE_HC_0 0x310 -#define MC_LATENCY_ALLOWANCE_DC_3 0x3c8 -#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac -#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4 -#define MC_LATENCY_ALLOWANCE_ISP2B_1 0x388 -#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328 -#define MC_LATENCY_ALLOWANCE_HDA_0 0x318 -#define MC_MIN_LENGTH_APE_0 0xb34 -#define MC_MIN_LENGTH_DCB_2 0x8a8 -#define MC_MIN_LENGTH_A9AVP_0 0x950 -#define MC_MIN_LENGTH_TSEC_0 0x93c -#define MC_MIN_LENGTH_DC_1 0x898 -#define MC_MIN_LENGTH_AXIAP_0 0x94c -#define MC_MIN_LENGTH_ISP2B_0 0x930 -#define MC_MIN_LENGTH_VI2_0 0x944 -#define MC_MIN_LENGTH_DCB_0 0x8a0 -#define MC_MIN_LENGTH_DCB_1 0x8a4 -#define MC_MIN_LENGTH_PPCS_1 0x8f4 -#define MC_MIN_LENGTH_NVJPG_0 0xb3c -#define MC_MIN_LENGTH_HDA_0 0x8c4 -#define MC_MIN_LENGTH_NVENC_0 0x8d4 -#define MC_MIN_LENGTH_SDMMC_0 0xb18 -#define MC_MIN_LENGTH_ISP2B_1 0x934 -#define MC_MIN_LENGTH_HC_1 0x8c0 -#define MC_MIN_LENGTH_DC_3 0xb20 -#define MC_MIN_LENGTH_AVPC_0 0x890 -#define MC_MIN_LENGTH_VIC_0 0x940 -#define MC_MIN_LENGTH_ISP2_0 0x91c -#define MC_MIN_LENGTH_HC_0 0x8bc -#define MC_MIN_LENGTH_SE_0 0xb38 -#define MC_MIN_LENGTH_NVDEC_0 0xb30 -#define MC_MIN_LENGTH_SATA_0 0x8fc -#define MC_MIN_LENGTH_DC_0 0x894 -#define MC_MIN_LENGTH_XUSB_1 0x92c -#define MC_MIN_LENGTH_DC_2 0x89c -#define MC_MIN_LENGTH_SDMMCAA_0 0xb14 -#define MC_MIN_LENGTH_GPU_0 0xb04 -#define MC_MIN_LENGTH_ETR_0 0xb44 -#define MC_MIN_LENGTH_AFI_0 0x88c -#define MC_MIN_LENGTH_PPCS_0 0x8f0 -#define MC_MIN_LENGTH_ISP2_1 0x920 -#define MC_MIN_LENGTH_XUSB_0 0x928 -#define MC_MIN_LENGTH_MPCORE_0 0x8cc -#define MC_MIN_LENGTH_TSECB_0 0xb48 -#define MC_MIN_LENGTH_SDMMCA_0 0xb10 -#define MC_MIN_LENGTH_GPU2_0 0xb40 -#define MC_MIN_LENGTH_SDMMCAB_0 0xb1c -#define MC_MIN_LENGTH_PTC_0 0x8f8 -#define MC_EMEM_ARB_OVERRIDE_1 0x968 -#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 0x984 -#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 0x988 -#define MC_EMEM_ARB_STATS_0 0x990 -#define MC_EMEM_ARB_STATS_1 0x994 -#define MC_MTS_CARVEOUT_BOM 0x9a0 -#define MC_MTS_CARVEOUT_SIZE_MB 0x9a4 -#define MC_MTS_CARVEOUT_ADR_HI 0x9a8 -#define MC_MTS_CARVEOUT_REG_CTRL 0x9ac -#define MC_ERR_MTS_STATUS 0x9b0 -#define MC_ERR_MTS_ADR 0x9b4 -#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 -#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 0xd74 -#define MC_SECURITY_CARVEOUT4_CFG0 0xcf8 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 0xd10 -#define MC_SECURITY_CARVEOUT4_SIZE_128KB 0xd04 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 0xc28 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 0xc30 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 0xc8c -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 0xd1c -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 0xd70 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 0xc2c -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 0xd7c -#define MC_SECURITY_CARVEOUT3_SIZE_128KB 0xcb4 -#define MC_SECURITY_CARVEOUT2_CFG0 0xc58 -#define MC_SECURITY_CARVEOUT1_CFG0 0xc08 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 0xc84 -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 0xc68 -#define MC_SECURITY_CARVEOUT3_BOM 0xcac -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 0xc70 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 0xd78 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 0xc7c -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 0xd18 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 0xcbc -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 0xc38 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 0xc34 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 0xcc0 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 0xd60 -#define MC_SECURITY_CARVEOUT3_CFG0 0xca8 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 0xcb8 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 0xc88 -#define MC_SECURITY_CARVEOUT2_SIZE_128KB 0xc64 -#define MC_SECURITY_CARVEOUT5_BOM_HI 0xd50 -#define MC_SECURITY_CARVEOUT1_SIZE_128KB 0xc14 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 0xd14 -#define MC_SECURITY_CARVEOUT1_BOM 0xc0c -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 0xd2c -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 0xd68 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 0xcc8 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 0xd58 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 0xd24 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 0xcc4 -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 0xc78 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 0xc1c -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 0xc18 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 0xd28 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 0xd5c -#define MC_SECURITY_CARVEOUT3_BOM_HI 0xcb0 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 0xcd8 -#define MC_SECURITY_CARVEOUT2_BOM_HI 0xc60 -#define MC_SECURITY_CARVEOUT4_BOM_HI 0xd00 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 0xd64 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 0xcdc -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 0xc80 -#define MC_SECURITY_CARVEOUT5_SIZE_128KB 0xd54 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 0xd20 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 0xcd4 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 0xd0c -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 0xc74 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 0xccc -#define MC_SECURITY_CARVEOUT4_BOM 0xcfc -#define MC_SECURITY_CARVEOUT5_CFG0 0xd48 -#define MC_SECURITY_CARVEOUT2_BOM 0xc5c -#define MC_SECURITY_CARVEOUT5_BOM 0xd4c -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 0xc24 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 0xd6c -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 0xcd0 -#define MC_SECURITY_CARVEOUT1_BOM_HI 0xc10 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 0xc20 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 0xc3c -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 0xc6c -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 0xd08 -#define MC_ERR_APB_ASID_UPDATE_STATUS 0x9d0 -#define MC_DA_CONFIG0 0x9dc - -/* Virtual aliases */ -#define VIRT_MC_SECURITY_CFG3 MAKE_MC_REG(MC_SECURITY_CFG3) - -/* Memory Controller clients */ -#define CLIENT_ACCESS_NUM_CLIENTS 32 -typedef enum { - /* _ACCESS0 */ - CSR_PTCR = (0 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0A = (1 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0AB = (2 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0B = (3 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0BB = (4 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0C = (5 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0CB = (6 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_AFIR = (14 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_AVPCARM7R = (15 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAYHC = (16 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAYHCB = (17 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HDAR = (21 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HOST1XDMAR = (22 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HOST1XR = (23 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_NVENCSRD = (28 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_PPCSAHBDMAR = (29 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_PPCSAHBSLVR = (30 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_SATAR = (31 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - - /* _ACCESS1 */ - CSR_VDEBSEVR = (34 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDEMBER = (35 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDEMCER = (36 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDETPER = (37 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_MPCORELPR = (38 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_MPCORER = (39 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_NVENCSWR = (43 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_AFIW = (49 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_AVPCARM7W = (50 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_HDAW = (53 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_HOST1XW = (54 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_MPCORELPW = (56 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_MPCOREW = (57 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_PPCSAHBDMAW = (59 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_PPCSAHBSLVW = (60 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_SATAW = (61 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_VDEBSEVW = (62 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_VDEDBGW = (63 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - - /* _ACCESS2 */ - CSW_VDEMBEW = (64 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_VDETPMW = (65 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_ISPRA = (68 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWA = (70 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWB = (71 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_XUSB_HOSTR = (74 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_XUSB_HOSTW = (75 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_XUSB_DEVR = (76 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_XUSB_DEVW = (77 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_ISPRAB = (78 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWAB = (80 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWBB = (81 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_TSECSRD = (84 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_TSECSWR = (85 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_A9AVPSCR = (86 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_A9AVPSCW = (87 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_GPUSRD = (88 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_GPUSWR = (89 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_DISPLAYT = (90 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - - /* _ACCESS3 */ - CSR_SDMMCRA = (96 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCRAA = (97 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCR = (98 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCRAB = (99 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWA = (100 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWAA = (101 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCW = (102 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWAB = (103 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_VICSRD = (108 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_VICSWR = (109 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_VIW = (114 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_DISPLAYD = (115 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_NVDECSRD = (120 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_NVDECSWR = (121 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_APER = (122 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_APEW = (123 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_NVJPGSRD = (126 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_NVJPGSWR = (127 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - - /* _ACCESS4 */ - CSR_SESRD = (128 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_SESWR = (129 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_AXIAPR = (130 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_AXIAPW = (131 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_ETRR = (132 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_ETRW = (133 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_TSECSRDB = (134 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_TSECSWRB = (135 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_GPUSRD2 = (136 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_GPUSWR2 = (137 - (CLIENT_ACCESS_NUM_CLIENTS * 4)) -} McClient; - -/* Memory Controller carveouts */ -#define CARVEOUT_ID_MIN 1 -#define CARVEOUT_ID_MAX 5 -typedef struct { - uint32_t config; - uint32_t paddr_low; - uint32_t paddr_high; - uint32_t size_big_pages; - uint32_t client_access_0; - uint32_t client_access_1; - uint32_t client_access_2; - uint32_t client_access_3; - uint32_t client_access_4; - uint32_t client_force_internal_access_0; - uint32_t client_force_internal_access_1; - uint32_t client_force_internal_access_2; - uint32_t client_force_internal_access_3; - uint32_t client_force_internal_access_4; - uint8_t padding[0x18]; -} security_carveout_t; - -void disable_bpmp_access_to_dram(void); - -#endif \ No newline at end of file diff --git a/exosphere/lp0fw/src/misc.c b/exosphere/lp0fw/src/misc.c deleted file mode 100644 index bab594d48..000000000 --- a/exosphere/lp0fw/src/misc.c +++ /dev/null @@ -1,52 +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 "misc.h" -#include "fuse.h" -#include "sysreg.h" -#include "pmc.h" - -void misc_configure_device_dbg_settings(void) { - /* Set APB_MISC_PP_CONFIG_CTL_TBE (enables RTCK daisy-chaining). */ - APB_MISC_PP_CONFIG_CTL_0 = 0x80; - - /* Configure JTAG and debug bits. */ - if (FUSE_CHIP_REGS->FUSE_SECURITY_MODE == 1) { - uint32_t secure_boot_val = 0b0100; /* Set NIDEN for aarch64. */ - uint32_t pp_config_ctl_val = 0x40; /* Set APB_MISC_PP_CONFIG_CTL_JTAG. */ - if (APBDEV_PMC_STICKY_BITS_0 & 0x40) { - pp_config_ctl_val = 0x0; - } else { - secure_boot_val = 0b1101; /* Set SPNIDEN, NIDEN, DBGEN for aarch64. */ - } - SB_PFCFG_0 = (SB_PFCFG_0 & ~0b1111) | secure_boot_val; /* Configure debug bits. */ - APB_MISC_PP_CONFIG_CTL_0 |= pp_config_ctl_val; /* Configure JTAG. */ - } - - /* Set HDA_LPBK_DIS if FUSE_SECURITY_MODE is set (disables HDA codec loopback). */ - APBDEV_PMC_STICKY_BITS_0 |= FUSE_CHIP_REGS->FUSE_SECURITY_MODE; - - /* Set E_INPUT in PINMUX_AUX_GPIO_PA6_0 (needed by the XUSB and SATA controllers). */ - PINMUX_AUX_GPIO_PA6_0 |= 0x40; -} - -void misc_restore_ram_svop(void) { - /* This sets CFG2TMC_RAM_SVOP_PDP to 0x2. */ - APB_MISC_GP_ASDBGREG_0 = (APB_MISC_GP_ASDBGREG_0 & 0xFCFFFFFF) | 0x02000000; -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/misc.h b/exosphere/lp0fw/src/misc.h deleted file mode 100644 index ccb17a773..000000000 --- a/exosphere/lp0fw/src/misc.h +++ /dev/null @@ -1,39 +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_MISC_H -#define EXOSPHERE_WARMBOOT_BIN_MISC_H - -#include <stdint.h> - -#include "utils.h" - -#define MISC_BASE (0x70000000) - -#define MAKE_MISC_REG(n) MAKE_REG32(MISC_BASE + n) - -#define APB_MISC_PP_CONFIG_CTL_0 MAKE_MISC_REG(0x024) -#define APB_MISC_GP_ASDBGREG_0 MAKE_MISC_REG(0x810) - -#define PINMUX_AUX_PWR_I2C_SCL_0 MAKE_MISC_REG(0x30DC) -#define PINMUX_AUX_PWR_I2C_SDA_0 MAKE_MISC_REG(0x30E0) -#define PINMUX_AUX_DVFS_PWM_0 MAKE_MISC_REG(0x3184) -#define PINMUX_AUX_GPIO_PA6_0 MAKE_MISC_REG(0x3244) - -void misc_configure_device_dbg_settings(void); -void misc_restore_ram_svop(void); - -#endif diff --git a/exosphere/lp0fw/src/pmc.h b/exosphere/lp0fw/src/pmc.h deleted file mode 100644 index a872c6073..000000000 --- a/exosphere/lp0fw/src/pmc.h +++ /dev/null @@ -1,61 +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_PMC_H -#define EXOSPHERE_WARMBOOT_BIN_PMC_H - -#include "utils.h" - -#define PMC_BASE (0x7000E400) - -#define MAKE_PMC_REG(ofs) (MAKE_REG32(PMC_BASE + ofs)) - -#define APBDEV_PMC_CNTRL_0 MAKE_PMC_REG(0x000) -#define APBDEV_PMC_DPD_SAMPLE_0 MAKE_PMC_REG(0x020) -#define APBDEV_PMC_DPD_ENABLE_0 MAKE_PMC_REG(0x024) -#define APBDEV_PMC_CLAMP_STATUS_0 MAKE_PMC_REG(0x02C) -#define APBDEV_PMC_PWRGATE_TOGGLE_0 MAKE_PMC_REG(0x030) -#define APBDEV_PMC_REMOVE_CLAMPING_CMD_0 MAKE_PMC_REG(0x034) -#define APBDEV_PMC_PWRGATE_STATUS_0 MAKE_PMC_REG(0x038) -#define APBDEV_PMC_SCRATCH12_0 MAKE_PMC_REG(0x080) -#define APBDEV_PMC_SCRATCH13_0 MAKE_PMC_REG(0x084) -#define APBDEV_PMC_SCRATCH18_0 MAKE_PMC_REG(0x098) -#define APBDEV_PMC_SCRATCH190_0 MAKE_PMC_REG(0x818) -#define APBDEV_PMC_OSC_EDPD_OVER_0 MAKE_PMC_REG(0x1A4) -#define APBDEV_PMC_STICKY_BITS_0 MAKE_PMC_REG(0x2C0) -#define APBDEV_PMC_SEC_DISABLE2_0 MAKE_PMC_REG(0x2C4) -#define APBDEV_PMC_WEAK_BIAS_0 MAKE_PMC_REG(0x2C8) -#define APBDEV_PMC_SECURE_SCRATCH21_0 MAKE_PMC_REG(0x334) -#define APBDEV_PMC_SECURE_SCRATCH24_0 MAKE_PMC_REG(0x340) -#define APBDEV_PMC_SECURE_SCRATCH25_0 MAKE_PMC_REG(0x344) -#define APBDEV_PMC_SECURE_SCRATCH26_0 MAKE_PMC_REG(0x348) -#define APBDEV_PMC_SECURE_SCRATCH27_0 MAKE_PMC_REG(0x34C) -#define APBDEV_PMC_SECURE_SCRATCH32_0 MAKE_PMC_REG(0x360) -#define APBDEV_PMC_SECURE_SCRATCH34_0 MAKE_PMC_REG(0x368) -#define APBDEV_PMC_SECURE_SCRATCH35_0 MAKE_PMC_REG(0x36C) -#define APBDEV_PMC_SECURE_SCRATCH112_0 MAKE_PMC_REG(0xB18) -#define APBDEV_PMC_SECURE_SCRATCH113_0 MAKE_PMC_REG(0xB1C) -#define APBDEV_PMC_SECURE_SCRATCH114_0 MAKE_PMC_REG(0xB20) -#define APBDEV_PMC_SECURE_SCRATCH115_0 MAKE_PMC_REG(0xB24) -#define APBDEV_PMC_FUSE_CTRL MAKE_PMC_REG(0x450) -#define APBDEV_PMC_IO_DPD3_REQ_0 MAKE_PMC_REG(0x45C) -#define APBDEV_PMC_IO_DPD3_STATUS_0 MAKE_PMC_REG(0x460) -#define APBDEV_PMC_IO_DPD4_REQ_0 MAKE_PMC_REG(0x464) -#define APBDEV_PMC_IO_DPD4_STATUS_0 MAKE_PMC_REG(0x468) -#define APBDEV_PMC_SET_SW_CLAMP_0 MAKE_PMC_REG(0x47C) -#define APBDEV_PMC_DDR_CNTRL_0 MAKE_PMC_REG(0x4E4) - -#endif diff --git a/exosphere/lp0fw/src/se.c b/exosphere/lp0fw/src/se.c deleted file mode 100644 index 2deccbdbc..000000000 --- a/exosphere/lp0fw/src/se.c +++ /dev/null @@ -1,266 +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 <string.h> - -#include "utils.h" -#include "lp0.h" -#include "se.h" - -static void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size); - -/* Initialize a SE linked list. */ -static void __attribute__((__noinline__)) ll_init(volatile se_ll_t *ll, void *buffer, size_t size) { - ll->num_entries = 0; /* 1 Entry. */ - - if (buffer != NULL) { - ll->addr_info.address = (uint32_t) buffer; - ll->addr_info.size = (uint32_t) size; - } else { - ll->addr_info.address = 0; - ll->addr_info.size = 0; - } -} - -void se_check_error_status_reg(void) { - if (se_get_regs()->SE_ERR_STATUS) { - reboot(); - } -} - -void se_check_for_error(void) { - volatile tegra_se_t *se = se_get_regs(); - if (se->SE_INT_STATUS & 0x10000 || se->SE_STATUS & 3 || se->SE_ERR_STATUS) { - reboot(); - } -} - -void se_verify_flags_cleared(void) { - if (se_get_regs()->SE_STATUS & 3) { - reboot(); - } -} - -void clear_aes_keyslot(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - reboot(); - } - - /* Zero out the whole keyslot and IV. */ - for (unsigned int i = 0; i < 0x10; i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | i; - se->SE_CRYPTO_KEYTABLE_DATA = 0; - } -} - -void clear_rsa_keyslot(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_RSA_MAX) { - reboot(); - } - - /* Zero out the whole keyslot. */ - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Modulus[i] */ - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | i | 0x40; - se->SE_RSA_KEYTABLE_DATA = 0; - } - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Expontent[i] */ - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | i; - se->SE_RSA_KEYTABLE_DATA = 0; - } -} - -void clear_aes_keyslot_iv(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - reboot(); - } - - for (size_t i = 0; i < (0x10 >> 2); i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | 8 | i; - se->SE_CRYPTO_KEYTABLE_DATA = 0; - } -} - -void decrypt_data_into_keyslot_256(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSLOT_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { - reboot(); - } - - se->SE_CONFIG = (0x202 << 16) | (ALG_AES_DEC | DST_KEYTAB); - se->SE_CRYPTO_CONFIG = keyslot_src << 24; - se->SE_CRYPTO_LAST_BLOCK = 0; - se->SE_CRYPTO_KEYTABLE_DST = keyslot_dst << 8; - - trigger_se_blocking_op(OP_START, NULL, 0, wrapped_key, wrapped_key_size); -} - -void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - se_ll_t in_ll; - se_ll_t out_ll; - - ll_init(&in_ll, (void *)src, src_size); - ll_init(&out_ll, dst, dst_size); - - /* Set the LLs. */ - se->SE_IN_LL_ADDR = (uint32_t)(&in_ll); - se->SE_OUT_LL_ADDR = (uint32_t) (&out_ll); - - /* Set registers for operation. */ - se->SE_ERR_STATUS = se->SE_ERR_STATUS; - se->SE_INT_STATUS = se->SE_INT_STATUS; - se->SE_OPERATION = op; - - while (!(se->SE_INT_STATUS & 0x10)) { /* Wait a while */ } - se_check_for_error(); -} - -/* Secure AES Functionality. */ -void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t block[0x10] = {0}; - - if (src_size > sizeof(block) || dst_size > sizeof(block)) { - reboot(); - } - - /* Load src data into block. */ - if (src_size != 0) { - memcpy(block, src, src_size); - } - - /* Trigger AES operation. */ - se_get_regs()->SE_CRYPTO_LAST_BLOCK = 0; - trigger_se_blocking_op(OP_START, block, sizeof(block), block, sizeof(block)); - - /* Copy output data into dst. */ - if (dst_size != 0) { - memcpy(dst, block, dst_size); - } -} - -void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - reboot(); - } - - /* Set configuration high (256-bit vs 128-bit) based on parameter. */ - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY) | (config_high << 16); - se->SE_CRYPTO_CONFIG = keyslot << 24 | 0x100; - se_perform_aes_block_operation(dst, 0x10, src, 0x10); -} - -void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - reboot(); - } - - se->SE_CONFIG = (ALG_AES_DEC | DST_MEMORY); - se->SE_CRYPTO_CONFIG = keyslot << 24; - se_perform_aes_block_operation(dst, 0x10, src, 0x10); -} - -void shift_left_xor_rb(uint8_t *key) { - uint8_t prev_high_bit = 0; - for (unsigned int i = 0; i < 0x10; i++) { - uint8_t cur_byte = key[0xF - i]; - key[0xF - i] = (cur_byte << 1) | (prev_high_bit); - prev_high_bit = cur_byte >> 7; - } - if (prev_high_bit) { - key[0xF] ^= 0x87; - } -} - -void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - reboot(); - } - - /* Generate the derived key, to be XOR'd with final output block. */ - uint8_t ALIGN(16) derived_key[0x10] = {0}; - se_aes_ecb_encrypt_block(keyslot, derived_key, sizeof(derived_key), derived_key, sizeof(derived_key), config_high); - shift_left_xor_rb(derived_key); - if (data_size & 0xF) { - shift_left_xor_rb(derived_key); - } - - se->SE_CONFIG = (ALG_AES_ENC | DST_HASHREG) | (config_high << 16); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | (0x145); - clear_aes_keyslot_iv(keyslot); - - unsigned int num_blocks = (data_size + 0xF) >> 4; - /* Handle aligned blocks. */ - if (num_blocks > 1) { - se->SE_CRYPTO_LAST_BLOCK = num_blocks - 2; - trigger_se_blocking_op(OP_START, NULL, 0, data, data_size); - se->SE_CRYPTO_CONFIG |= 0x80; - } - - /* Create final block. */ - uint8_t ALIGN(16) last_block[0x10] = {0}; - if (data_size & 0xF) { - memcpy(last_block, data + (data_size & ~0xF), data_size & 0xF); - last_block[data_size & 0xF] = 0x80; /* Last block = data || 100...0 */ - } else if (data_size >= 0x10) { - memcpy(last_block, data + data_size - 0x10, 0x10); - } - - for (unsigned int i = 0; i < 0x10; i++) { - last_block[i] ^= derived_key[i]; - } - - /* Perform last operation. */ - se->SE_CRYPTO_LAST_BLOCK = 0; - trigger_se_blocking_op(OP_START, NULL, 0, last_block, sizeof(last_block)); - - /* Copy output CMAC. */ - for (unsigned int i = 0; i < (cmac_size >> 2); i++) { - ((uint32_t *)cmac)[i] = ((volatile uint32_t *)se->SE_HASH_RESULT)[i]; - } -} - -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { - se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0x202); -} - -void se_aes_256_cbc_decrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || src_size < 0x10) { - reboot(); - } - - se->SE_CONFIG = (ALG_AES_DEC | DST_MEMORY) | (0x202 << 16); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x66; - clear_aes_keyslot_iv(keyslot); - se->SE_CRYPTO_LAST_BLOCK = (src_size >> 4) - 1; - trigger_se_blocking_op(OP_START, dst, dst_size, src, src_size); -} \ No newline at end of file diff --git a/exosphere/lp0fw/src/se.h b/exosphere/lp0fw/src/se.h deleted file mode 100644 index 5e0083e36..000000000 --- a/exosphere/lp0fw/src/se.h +++ /dev/null @@ -1,179 +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_SE_H -#define EXOSPHERE_WARMBOOT_BIN_SE_H - -#define SE_BASE 0x70012000 -#define MAKE_SE_REG(n) MAKE_REG32(SE_BASE + n) - -#define KEYSLOT_SWITCH_LP0TZRAMKEK 0x2 -#define KEYSLOT_SWITCH_LP0TZRAMKEY 0x3 -#define KEYSLOT_SWITCH_SRKGENKEY 0x8 -#define KEYSLOT_SWITCH_PACKAGE2KEY 0x8 -#define KEYSLOT_SWITCH_TEMPKEY 0x9 -#define KEYSLOT_SWITCH_SESSIONKEY 0xA -#define KEYSLOT_SWITCH_RNGKEY 0xB -#define KEYSLOT_SWITCH_MASTERKEY 0xC -#define KEYSLOT_SWITCH_DEVICEKEY 0xD - -/* This keyslot was added in 4.0.0. */ -#define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD -#define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE -#define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF - -/* This keyslot was added in 5.0.0. */ -#define KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY 0xA - -#define KEYSLOT_AES_MAX 0x10 -#define KEYSLOT_RSA_MAX 0x2 - -#define KEYSIZE_AES_MAX 0x20 -#define KEYSIZE_RSA_MAX 0x100 - -#define ALG_SHIFT (12) -#define ALG_DEC_SHIFT (8) -#define ALG_NOP (0 << ALG_SHIFT) -#define ALG_AES_ENC (1 << ALG_SHIFT) -#define ALG_AES_DEC ((1 << ALG_DEC_SHIFT) | ALG_NOP) -#define ALG_RNG (2 << ALG_SHIFT) -#define ALG_SHA (3 << ALG_SHIFT) -#define ALG_RSA (4 << ALG_SHIFT) - -#define DST_SHIFT (2) -#define DST_MEMORY (0 << DST_SHIFT) -#define DST_HASHREG (1 << DST_SHIFT) -#define DST_KEYTAB (2 << DST_SHIFT) -#define DST_SRK (3 << DST_SHIFT) -#define DST_RSAREG (4 << DST_SHIFT) - -#define ENCMODE_SHIFT (24) -#define DECMODE_SHIFT (16) -#define ENCMODE_SHA256 (5 << ENCMODE_SHIFT) - -#define HASH_DISABLE (0x0) -#define HASH_ENABLE (0x1) - -#define OP_ABORT 0 -#define OP_START 1 -#define OP_RESTART 2 -#define OP_CTX_SAVE 3 -#define OP_RESTART_IN 4 - -#define CTX_SAVE_SRC_SHIFT 29 -#define CTX_SAVE_SRC_STICKY_BITS (0 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_KEYTABLE_AES (2 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_KEYTABLE_RSA (1 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_MEM (4 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_SRK (6 << CTX_SAVE_SRC_SHIFT) - -#define CTX_SAVE_KEY_LOW_BITS 0 -#define CTX_SAVE_KEY_HIGH_BITS 1 -#define CTX_SAVE_KEY_ORIGINAL_IV 2 -#define CTX_SAVE_KEY_UPDATED_IV 3 - -#define CTX_SAVE_STICKY_BIT_INDEX_SHIFT 24 -#define CTX_SAVE_KEY_INDEX_SHIFT 8 -#define CTX_SAVE_RSA_KEY_INDEX_SHIFT 16 -#define CTX_SAVE_RSA_KEY_BLOCK_INDEX_SHIFT 12 - -#define RSA_2048_BYTES 0x100 - -typedef struct { - uint32_t SE_SE_SECURITY; - uint32_t SE_TZRAM_SECURITY; - uint32_t SE_OPERATION; - uint32_t SE_INT_ENABLE; - uint32_t SE_INT_STATUS; - uint32_t SE_CONFIG; - uint32_t SE_IN_LL_ADDR; - uint32_t SE_IN_CUR_BYTE_ADDR; - uint32_t SE_IN_CUR_LL_ID; - uint32_t SE_OUT_LL_ADDR; - uint32_t SE_OUT_CUR_BYTE_ADDR; - uint32_t SE_OUT_CUR_LL_ID; - uint32_t SE_HASH_RESULT[0x10]; - uint32_t SE_CTX_SAVE_CONFIG; - uint32_t _0x74[0x63]; - uint32_t SE_SHA_CONFIG; - uint32_t SE_SHA_MSG_LENGTH[0x4]; - uint32_t SE_SHA_MSG_LEFT[0x4]; - uint32_t _0x224[0x17]; - uint32_t SE_CRYPTO_SECURITY_PERKEY; - uint32_t SE_CRYPTO_KEYTABLE_ACCESS[0x10]; - uint32_t _0x2C4[0x10]; - uint32_t SE_CRYPTO_CONFIG; - uint32_t SE_CRYPTO_LINEAR_CTR[0x4]; - uint32_t SE_CRYPTO_LAST_BLOCK; - uint32_t SE_CRYPTO_KEYTABLE_ADDR; - uint32_t SE_CRYPTO_KEYTABLE_DATA; - uint32_t _0x324[0x3]; - uint32_t SE_CRYPTO_KEYTABLE_DST; - uint32_t _0x334[0x3]; - uint32_t SE_RNG_CONFIG; - uint32_t SE_RNG_SRC_CONFIG; - uint32_t SE_RNG_RESEED_INTERVAL; - uint32_t _0x34C[0x2D]; - uint32_t SE_RSA_CONFIG; - uint32_t SE_RSA_KEY_SIZE; - uint32_t SE_RSA_EXP_SIZE; - uint32_t SE_RSA_SECURITY_PERKEY; - uint32_t SE_RSA_KEYTABLE_ACCESS[0x2]; - uint32_t _0x418[0x2]; - uint32_t SE_RSA_KEYTABLE_ADDR; - uint32_t SE_RSA_KEYTABLE_DATA; - uint32_t SE_RSA_OUTPUT[0x40]; - uint32_t _0x528[0xB6]; - uint32_t SE_STATUS; - uint32_t SE_ERR_STATUS; - uint32_t SE_MISC; - uint32_t SE_SPARE; - uint32_t SE_ENTROPY_DEBUG_COUNTER; - uint32_t _0x814; - uint32_t _0x818; - uint32_t _0x81C; - uint32_t _0x820[0x5F8]; -} tegra_se_t; - -typedef struct { - uint32_t address; - uint32_t size; -} se_addr_info_t; - -typedef struct { - uint32_t num_entries; /* Set to total entries - 1 */ - se_addr_info_t addr_info; /* This should really be an array...but for our use case it works. */ -} se_ll_t; - -static inline volatile tegra_se_t *se_get_regs(void) { - return (volatile tegra_se_t *)SE_BASE; -} - -void se_check_error_status_reg(void); -void se_check_for_error(void); - -void se_verify_flags_cleared(void); - -void clear_aes_keyslot(unsigned int keyslot); -void clear_rsa_keyslot(unsigned int keyslot); -void clear_aes_keyslot_iv(unsigned int keyslot); - -void decrypt_data_into_keyslot_256(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size); - -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); -void se_aes_256_cbc_decrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); - -#endif diff --git a/exosphere/lp0fw/src/secmon.c b/exosphere/lp0fw/src/secmon.c deleted file mode 100644 index 01a6ff539..000000000 --- a/exosphere/lp0fw/src/secmon.c +++ /dev/null @@ -1,129 +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 "lp0.h" -#include "secmon.h" -#include "se.h" -#include "fuse.h" -#include "pmc.h" - -/* "private" functions. */ -static bool secmon_should_clear_aes_keyslot(unsigned int keyslot); -static void secmon_clear_unused_keyslots(void); -static void secmon_decrypt_saved_image(void *dst, const void *src, size_t size); - -void secmon_restore_to_tzram(const uint32_t target_firmware) { - /* Newer warmboot binaries clear the untouched keyslots for safety. */ - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - secmon_clear_unused_keyslots(); - } - - /* Decrypt Secure Monitor from DRAM into TZRAM. */ - void *tzram_src = (void *)(0x80010000); - void *tzram_dst = (void *)(target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 ? 0x7C012000 : 0x7C010000); - const size_t tzram_size = 0xE000; - secmon_decrypt_saved_image(tzram_dst, tzram_src, tzram_size); - - /* Nintendo clears DRAM, but I'm not sure why, given they lock out BPMP access to DRAM. */ - for (size_t i = 0; i < tzram_size/sizeof(uint32_t); i++) { - ((volatile uint32_t *)tzram_src)[i] = 0; - } - - /* Make security engine require secure busmaster. */ - se_get_regs()->SE_TZRAM_SECURITY = 0; - - /* TODO: se_verify_keys_unreadable(); */ - - /* TODO: pmc_lockout_wb_scratch_registers(); */ - - /* Disable fuse programming. */ - fuse_disable_programming(); -} - -void secmon_decrypt_saved_image(void *dst, const void *src, size_t size) { - /* Derive the key used for context save. */ - { - const uint32_t key_source[4] = { APBDEV_PMC_SECURE_SCRATCH24_0, APBDEV_PMC_SECURE_SCRATCH25_0, APBDEV_PMC_SECURE_SCRATCH26_0, APBDEV_PMC_SECURE_SCRATCH27_0 }; - - clear_aes_keyslot(KEYSLOT_SWITCH_LP0TZRAMKEY); - decrypt_data_into_keyslot_256(KEYSLOT_SWITCH_LP0TZRAMKEY, KEYSLOT_SWITCH_LP0TZRAMKEK, key_source, sizeof(key_source)); - - clear_aes_keyslot(KEYSLOT_SWITCH_LP0TZRAMKEK); - APBDEV_PMC_SECURE_SCRATCH24_0 = 0; - APBDEV_PMC_SECURE_SCRATCH25_0 = 0; - APBDEV_PMC_SECURE_SCRATCH26_0 = 0; - APBDEV_PMC_SECURE_SCRATCH27_0 = 0; - } - - /* First, AES-256-CBC decrypt the image into TZRAM. */ - se_aes_256_cbc_decrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, dst, size, src, size); - - /* Next, calculate CMAC. */ - uint32_t tzram_cmac[4] = {0, 0, 0, 0}; - se_compute_aes_256_cmac(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_cmac, sizeof(tzram_cmac), dst, size); - - /* Validate the MAC against saved one in PMC scratch. */ - if (tzram_cmac[0] != APBDEV_PMC_SECURE_SCRATCH112_0 || - tzram_cmac[1] != APBDEV_PMC_SECURE_SCRATCH113_0 || - tzram_cmac[2] != APBDEV_PMC_SECURE_SCRATCH114_0 || - tzram_cmac[3] != APBDEV_PMC_SECURE_SCRATCH115_0) { - reboot(); - } - - /* Clear the PMC scratch registers that hold the CMAC. */ - APBDEV_PMC_SECURE_SCRATCH112_0 = 0; - APBDEV_PMC_SECURE_SCRATCH113_0 = 0; - APBDEV_PMC_SECURE_SCRATCH114_0 = 0; - APBDEV_PMC_SECURE_SCRATCH115_0 = 0; - - /* Clear keyslot now that we're done with it. */ - clear_aes_keyslot(KEYSLOT_SWITCH_LP0TZRAMKEY); -} - -bool secmon_should_clear_aes_keyslot(unsigned int keyslot) { - /* We'll just compare keyslot against a hardcoded list of keys. */ - static const uint8_t saved_keyslots[6] = { - KEYSLOT_SWITCH_LP0TZRAMKEK, - KEYSLOT_SWITCH_SESSIONKEY, - KEYSLOT_SWITCH_RNGKEY, - KEYSLOT_SWITCH_MASTERKEY, - KEYSLOT_SWITCH_DEVICEKEY, - KEYSLOT_SWITCH_4XOLDDEVICEKEY - }; - - for (unsigned int i = 0; i < sizeof(saved_keyslots)/sizeof(saved_keyslots[0]); i++) { - if (keyslot == saved_keyslots[i]) { - return false; - } - } - return true; -} - -void secmon_clear_unused_keyslots(void) { - /* Clear unused keyslots. */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - if (secmon_should_clear_aes_keyslot(i)) { - clear_aes_keyslot(i); - } - clear_aes_keyslot_iv(i); - } - for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { - clear_rsa_keyslot(i); - } -} diff --git a/exosphere/lp0fw/src/secmon.h b/exosphere/lp0fw/src/secmon.h deleted file mode 100644 index 2577f3855..000000000 --- a/exosphere/lp0fw/src/secmon.h +++ /dev/null @@ -1,26 +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_SECMON_H -#define EXOSPHERE_WARMBOOT_BIN_SECMON_H - -#include <stdint.h> - -#include "utils.h" - -void secmon_restore_to_tzram(const uint32_t target_firmware); - -#endif diff --git a/exosphere/lp0fw/src/start.s b/exosphere/lp0fw/src/start.s deleted file mode 100644 index 5a458e123..000000000 --- a/exosphere/lp0fw/src/start.s +++ /dev/null @@ -1,65 +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/>. - */ - -.section .text.start - -/* Warmboot header. */ -/* Binary size */ -.word __total_size__ -.rept 3 -.word 0x00000000 -.endr -/* RSA modulus */ -.rept 0x40 -.word 0xFFFFFFFF -.endr -/* Padding */ -.rept 4 -.word 0x00000000 -.endr -/* RSA signature */ -.rept 0x40 -.word 0xFFFFFFFF -.endr -/* Padding */ -.rept 4 -.word 0x00000000 -.endr -/* Relocation meta */ -.word __total_size__ -.word _start -.word _start -.word __executable_size__ - -.global _start -_start: - b crt0 - -.global _metadata -_metadata: - .ascii "WBT0" /* Magic number */ - .word 0x00000000 /* Target firmware. */ - .word 0x00000000 /* Reserved */ - .word 0x00000000 /* Reserved */ - -.global crt0 -.type crt0, %function -crt0: - @ setup to call lp0_entry_main - ldr sp, =__stack_top__ - ldr lr, =reboot - ldr r0, =_metadata - b lp0_entry_main diff --git a/exosphere/lp0fw/src/sysreg.h b/exosphere/lp0fw/src/sysreg.h deleted file mode 100644 index 10af8357b..000000000 --- a/exosphere/lp0fw/src/sysreg.h +++ /dev/null @@ -1,46 +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_SYSREG_H -#define EXOSPHERE_WARMBOOT_BIN_SYSREG_H - -#include <stdint.h> - - -#define SYSREG_BASE (0x6000C000) - -#define SB_BASE (SYSREG_BASE + 0x200) - -#define MAKE_SYSREG(n) MAKE_REG32(SYSREG_BASE + n) -#define MAKE_SB_REG(n) MAKE_REG32(SB_BASE + n) - -#define AHB_ARBITRATION_DISABLE_0 MAKE_SYSREG(0x004) - -#define SB_CSR_0 MAKE_SB_REG(0x00) -#define SB_PIROM_START_0 MAKE_SB_REG(0x04) -#define SB_PFCFG_0 MAKE_SB_REG(0x08) -#define SB_SECURE_SPAREREG_0_0 MAKE_SB_REG(0x0C) -#define SB_SECURE_SPAREREG_1_0 MAKE_SB_REG(0x10) -#define SB_SECURE_SPAREREG_2_0 MAKE_SB_REG(0x14) -#define SB_SECURE_SPAREREG_3_0 MAKE_SB_REG(0x18) -#define SB_SECURE_SPAREREG_4_0 MAKE_SB_REG(0x1C) -#define SB_SECURE_SPAREREG_5_0 MAKE_SB_REG(0x20) -#define SB_SECURE_SPAREREG_6_0 MAKE_SB_REG(0x24) -#define SB_SECURE_SPAREREG_7_0 MAKE_SB_REG(0x28) -#define SB_AA64_RESET_LOW_0 MAKE_SB_REG(0x30) -#define SB_AA64_RESET_HIGH_0 MAKE_SB_REG(0x34) - -#endif diff --git a/exosphere/lp0fw/src/timer.h b/exosphere/lp0fw/src/timer.h deleted file mode 100644 index ec2d3dfe6..000000000 --- a/exosphere/lp0fw/src/timer.h +++ /dev/null @@ -1,34 +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_TIMER_H -#define EXOSPHERE_WARMBOOT_BIN_TIMER_H - -#include "utils.h" - -#define TIMERUS_CNTR_1US_0 MAKE_REG32(0x60005010) -#define TIMERUS_USEC_CFG_0 MAKE_REG32(0x60005014) - -static inline void timer_wait(uint32_t microseconds) { - const uint32_t old_time = TIMERUS_CNTR_1US_0; - while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { - /* Spin-lock. */ - } -} - -void spinlock_wait(uint32_t count); - -#endif diff --git a/exosphere/lp0fw/src/utils.h b/exosphere/lp0fw/src/utils.h deleted file mode 100644 index 88fd917c8..000000000 --- a/exosphere/lp0fw/src/utils.h +++ /dev/null @@ -1,39 +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_UTILS_H -#define EXOSPHERE_WARMBOOT_BIN_UTILS_H - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <vapours/ams_version.h> - -#define BIT(n) (1u << (n)) -#define BITL(n) (1ull << (n)) -#define MASK(n) (BIT(n) - 1) -#define MASKL(n) (BITL(n) - 1) -#define MASK2(a,b) (MASK(a) & ~MASK(b)) -#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) - -#define MAKE_REG32(a) (*(volatile uint32_t *)(a)) - -#define ALIGN(m) __attribute__((aligned(m))) -#define PACKED __attribute__((packed)) - -#define ALINLINE __attribute__((always_inline)) - -#endif diff --git a/exosphere/rebootstub/Makefile b/exosphere/rebootstub/Makefile deleted file mode 100644 index ff265beef..000000000 --- a/exosphere/rebootstub/Makefile +++ /dev/null @@ -1,154 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/base_rules - -#--------------------------------------------------------------------------------- -# 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 -DATA := data -INCLUDES := include - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork - -CFLAGS := \ - -g \ - -Os \ - -ffunction-sections \ - -fdata-sections \ - -fomit-frame-pointer \ - -fno-inline \ - -std=gnu11 \ - -Werror \ - -Wall \ - $(ARCH) $(DEFINES) - -CFLAGS += $(INCLUDE) -D__BPMP__ - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := - - -#--------------------------------------------------------------------------------- -# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -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)/*.*))) - -#--------------------------------------------------------------------------------- -# 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) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) - -$(BUILD): - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#--------------------------------------------------------------------------------- -clean: - @echo 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: $(OFILES) - @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 -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere/rebootstub/linker.ld b/exosphere/rebootstub/linker.ld deleted file mode 100644 index d2e018d4d..000000000 --- a/exosphere/rebootstub/linker.ld +++ /dev/null @@ -1,18 +0,0 @@ -OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") -OUTPUT_ARCH(arm) - -ENTRY(_start) -SECTIONS -{ - . = 0x4003F000; - - __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(.); -} \ No newline at end of file diff --git a/exosphere/rebootstub/linker.specs b/exosphere/rebootstub/linker.specs deleted file mode 100644 index 300990418..000000000 --- a/exosphere/rebootstub/linker.specs +++ /dev/null @@ -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 diff --git a/exosphere/rebootstub/src/i2c.c b/exosphere/rebootstub/src/i2c.c deleted file mode 100644 index fc380962b..000000000 --- a/exosphere/rebootstub/src/i2c.c +++ /dev/null @@ -1,169 +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 "max77620.h" -#include "i2c.h" -#include "timer.h" - -/* Prototypes for internal commands. */ -void i2c_load_config(void); -int i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes); -int i2c_read(unsigned int device, void *dst, unsigned num_bytes); -int i2c_query(uint8_t device, uint8_t r, void *dst, size_t num_bytes); -int i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b); - -/* Load hardware config for I2C4. */ -void i2c_load_config(void) { - /* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */ - I2C_I2C_CONFIG_LOAD_0 = 0x25; - - /* Wait a bit for master config to be loaded. */ - for (unsigned int i = 0; i < 20; i++) { - timer_wait(1); - if (!(I2C_I2C_CONFIG_LOAD_0 & 1)) { - break; - } - } -} - -/* Initialize I2C4. */ -void i2c_init(void) { - /* Setup divisor, and clear the bus. */ - I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001; - I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003; - - /* Load hardware configuration. */ - i2c_load_config(); - - /* Wait a while until BUS_CLEAR_DONE is set. */ - for (unsigned int i = 0; i < 10; i++) { - timer_wait(20000); - if (I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) { - break; - } - } - - /* Read the BUS_CLEAR_STATUS. Result doesn't matter. */ - I2C_I2C_BUS_CLEAR_STATUS_0; - - /* Read and set the Interrupt Status. */ - uint32_t int_status = I2C_INTERRUPT_STATUS_REGISTER_0; - I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; -} - -/* Writes a value to an i2c device. */ -int i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes) { - if (num_bytes > 4) { - return 0; - } - - /* Set device for 7-bit mode. */ - I2C_I2C_CMD_ADDR0_0 = device << 1; - - /* Load in data to write. */ - I2C_I2C_CMD_DATA1_0 = val; - - /* Set config with LENGTH = num_bytes, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2800; - - i2c_load_config(); - - /* Config |= SEND; */ - I2C_I2C_CNFG_0 |= 0x200; - - - while (I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */ - return (I2C_I2C_STATUS_0 & 0xF) == 0; -} - -/* Reads a value from an i2c device. */ -int i2c_read(unsigned device, void *dst, unsigned num_bytes) { - if (num_bytes > 4) { - return 0; - } - - /* Set device for 7-bit read mode. */ - I2C_I2C_CMD_ADDR0_0 = (device << 1) | 1; - - /* Set config with LENGTH = num_bytes, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2840; - - i2c_load_config(); - - /* Config |= SEND; */ - I2C_I2C_CNFG_0 |= 0x200; - - - while (I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Ensure success. */ - if ((I2C_I2C_STATUS_0 & 0xF) != 0) { - return 0; - } - - uint32_t val = I2C_I2C_CMD_DATA1_0; - for (size_t i = 0; i < num_bytes; i++) { - ((uint8_t *)dst)[i] = ((uint8_t *)&val)[i]; - } - return 1; -} - -/* Queries the value of a register. */ -int i2c_query(uint8_t device, uint8_t r, void *dst, size_t num_bytes) { - /* Limit output size to 32-bits. */ - if (num_bytes > 4) { - return 0; - } - - /* Write single byte register ID to device. */ - if (!i2c_write(device, r, 1)) { - return 0; - } - - return i2c_read(device, dst, num_bytes); - -} - -/* Writes a byte val to reg for given device. */ -int i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b) { - uint32_t val = (reg) | (b << 8); - /* Write 1 byte (reg) + 1 byte (value) */ - return i2c_write(device, val, 2); -} - -void i2c_stop_rtc_alarm(void) { - i2c_send_byte_command(MAX77620_RTC_I2C_ADDR, MAX77620_REG_RTCUPDATE0, 0x10); - - uint8_t val = 0; - for (int i = 0; i < 14; i++) { - if (i2c_query(MAX77620_RTC_I2C_ADDR, 0x0E + i, &val, 1)) { - val &= 0x7F; - i2c_send_byte_command(MAX77620_RTC_I2C_ADDR, 0x0E + i, val); - } - } - - i2c_send_byte_command(MAX77620_RTC_I2C_ADDR, MAX77620_REG_RTCUPDATE0, 0x01); -} - -void i2c_send_shutdown_cmd(void) { - i2c_send_byte_command(MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_PWR_OFF); -} diff --git a/exosphere/rebootstub/src/i2c.h b/exosphere/rebootstub/src/i2c.h deleted file mode 100644 index 480ff41b8..000000000 --- a/exosphere/rebootstub/src/i2c.h +++ /dev/null @@ -1,51 +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_REBOOTSTUB_I2C_H -#define EXOSPHERE_REBOOTSTUB_I2C_H - -#include "utils.h" - -/* I2C_BASE = I2C4. */ -#define I2C_BASE (0x7000D000) - -#define MAKE_I2C_REG(ofs) (MAKE_REG32(I2C_BASE + ofs)) - -#define I2C_I2C_CNFG_0 MAKE_I2C_REG(0x000) - -#define I2C_I2C_CMD_ADDR0_0 MAKE_I2C_REG(0x004) - -#define I2C_I2C_CMD_DATA1_0 MAKE_I2C_REG(0x00C) - -#define I2C_I2C_STATUS_0 MAKE_I2C_REG(0x01C) - -#define I2C_INTERRUPT_STATUS_REGISTER_0 MAKE_I2C_REG(0x068) - -#define I2C_I2C_CLK_DIVISOR_REGISTER_0 MAKE_I2C_REG(0x06C) - -#define I2C_I2C_BUS_CLEAR_CONFIG_0 MAKE_I2C_REG(0x084) - -#define I2C_I2C_BUS_CLEAR_STATUS_0 MAKE_I2C_REG(0x088) - - -#define I2C_I2C_CONFIG_LOAD_0 MAKE_I2C_REG(0x08C) - -void i2c_init(void); - -void i2c_stop_rtc_alarm(void); -void i2c_send_shutdown_cmd(void); - -#endif diff --git a/exosphere/rebootstub/src/max77620.h b/exosphere/rebootstub/src/max77620.h deleted file mode 100644 index 6cbaaa94e..000000000 --- a/exosphere/rebootstub/src/max77620.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Defining registers address and its bit definitions of MAX77620 and MAX20024 - * - * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. - * 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. - */ - -#ifndef _MFD_MAX77620_H_ -#define _MFD_MAX77620_H_ - -#define MAX77620_I2C_ADDR 0x3C -#define MAX77620_RTC_I2C_ADDR 0x68 - -/* RTC Registers */ -#define MAX77620_REG_RTCINT 0x00 -#define MAX77620_REG_RTCINTM 0x01 -#define MAX77620_REG_RTCCNTLM 0x02 -#define MAX77620_REG_RTCCNTL 0x03 -#define MAX77620_REG_RTCUPDATE0 0x04 -#define MAX77620_REG_RTCUPDATE1 0x05 -#define MAX77620_REG_RTCSMPL 0x06 -#define MAX77620_REG_RTCSEC 0x07 -#define MAX77620_REG_RTCMIN 0x08 -#define MAX77620_REG_RTCHOUR 0x09 -#define MAX77620_REG_RTCDOW 0x0A -#define MAX77620_REG_RTCMONTH 0x0B -#define MAX77620_REG_RTCYEAR 0x0C -#define MAX77620_REG_RTCDOM 0x0D -#define MAX77620_REG_RTCSECA1 0x0E -#define MAX77620_REG_RTCMINA1 0x0F -#define MAX77620_REG_RTCHOURA1 0x10 -#define MAX77620_REG_RTCDOWA1 0x11 -#define MAX77620_REG_RTCMONTHA1 0x12 -#define MAX77620_REG_RTCYEARA1 0x13 -#define MAX77620_REG_RTCDOMA1 0x14 -#define MAX77620_REG_RTCSECA2 0x15 -#define MAX77620_REG_RTCMINA2 0x16 -#define MAX77620_REG_RTCHOURA2 0x17 -#define MAX77620_REG_RTCDOWA2 0x18 -#define MAX77620_REG_RTCMONTHA2 0x19 -#define MAX77620_REG_RTCYEARA2 0x1A -#define MAX77620_REG_RTCDOMA2 0x1B - -/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ -#define MAX77620_REG_CNFGGLBL1 0x00 -#define MAX77620_REG_CNFGGLBL2 0x01 -#define MAX77620_REG_CNFGGLBL3 0x02 -#define MAX77620_REG_CNFG1_32K 0x03 -#define MAX77620_REG_CNFGBBC 0x04 -#define MAX77620_REG_IRQTOP 0x05 -#define MAX77620_REG_INTLBT 0x06 -#define MAX77620_REG_IRQSD 0x07 -#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 -#define MAX77620_REG_IRQ_LVL2_L8 0x09 -#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A -#define MAX77620_REG_ONOFFIRQ 0x0B -#define MAX77620_REG_NVERC 0x0C -#define MAX77620_REG_IRQTOPM 0x0D -#define MAX77620_REG_INTENLBT 0x0E -#define MAX77620_REG_IRQMASKSD 0x0F -#define MAX77620_REG_IRQ_MSK_L0_7 0x10 -#define MAX77620_REG_IRQ_MSK_L8 0x11 -#define MAX77620_REG_ONOFFIRQM 0x12 -#define MAX77620_REG_STATLBT 0x13 -#define MAX77620_REG_STATSD 0x14 -#define MAX77620_REG_ONOFFSTAT 0x15 - -/* SD and LDO Registers */ -#define MAX77620_REG_SD0 0x16 -#define MAX77620_REG_SD1 0x17 -#define MAX77620_REG_SD2 0x18 -#define MAX77620_REG_SD3 0x19 -#define MAX77620_REG_SD4 0x1A -#define MAX77620_REG_DVSSD0 0x1B -#define MAX77620_REG_DVSSD1 0x1C -#define MAX77620_REG_SD0_CFG 0x1D -#define MAX77620_REG_SD1_CFG 0x1E -#define MAX77620_REG_SD2_CFG 0x1F -#define MAX77620_REG_SD3_CFG 0x20 -#define MAX77620_REG_SD4_CFG 0x21 -#define MAX77620_REG_SD_CFG2 0x22 -#define MAX77620_REG_LDO0_CFG 0x23 -#define MAX77620_REG_LDO0_CFG2 0x24 -#define MAX77620_REG_LDO1_CFG 0x25 -#define MAX77620_REG_LDO1_CFG2 0x26 -#define MAX77620_REG_LDO2_CFG 0x27 -#define MAX77620_REG_LDO2_CFG2 0x28 -#define MAX77620_REG_LDO3_CFG 0x29 -#define MAX77620_REG_LDO3_CFG2 0x2A -#define MAX77620_REG_LDO4_CFG 0x2B -#define MAX77620_REG_LDO4_CFG2 0x2C -#define MAX77620_REG_LDO5_CFG 0x2D -#define MAX77620_REG_LDO5_CFG2 0x2E -#define MAX77620_REG_LDO6_CFG 0x2F -#define MAX77620_REG_LDO6_CFG2 0x30 -#define MAX77620_REG_LDO7_CFG 0x31 -#define MAX77620_REG_LDO7_CFG2 0x32 -#define MAX77620_REG_LDO8_CFG 0x33 -#define MAX77620_REG_LDO8_CFG2 0x34 -#define MAX77620_REG_LDO_CFG3 0x35 - -#define MAX77620_LDO_SLEW_RATE_MASK 0x1 - -/* LDO Configuration 3 */ -#define MAX77620_TRACK4_MASK (1 << 5) -#define MAX77620_TRACK4_SHIFT 5 - -/* Voltage */ -#define MAX77620_SDX_VOLT_MASK 0xFF -#define MAX77620_SD0_VOLT_MASK 0x3F -#define MAX77620_SD1_VOLT_MASK 0x7F -#define MAX77620_LDO_VOLT_MASK 0x3F - -#define MAX77620_REG_GPIO0 0x36 -#define MAX77620_REG_GPIO1 0x37 -#define MAX77620_REG_GPIO2 0x38 -#define MAX77620_REG_GPIO3 0x39 -#define MAX77620_REG_GPIO4 0x3A -#define MAX77620_REG_GPIO5 0x3B -#define MAX77620_REG_GPIO6 0x3C -#define MAX77620_REG_GPIO7 0x3D -#define MAX77620_REG_PUE_GPIO 0x3E -#define MAX77620_REG_PDE_GPIO 0x3F -#define MAX77620_REG_AME_GPIO 0x40 -#define MAX77620_REG_ONOFFCNFG1 0x41 -#define MAX77620_REG_ONOFFCNFG2 0x42 - -/* FPS Registers */ -#define MAX77620_REG_FPS_CFG0 0x43 -#define MAX77620_REG_FPS_CFG1 0x44 -#define MAX77620_REG_FPS_CFG2 0x45 -#define MAX77620_REG_FPS_LDO0 0x46 -#define MAX77620_REG_FPS_LDO1 0x47 -#define MAX77620_REG_FPS_LDO2 0x48 -#define MAX77620_REG_FPS_LDO3 0x49 -#define MAX77620_REG_FPS_LDO4 0x4A -#define MAX77620_REG_FPS_LDO5 0x4B -#define MAX77620_REG_FPS_LDO6 0x4C -#define MAX77620_REG_FPS_LDO7 0x4D -#define MAX77620_REG_FPS_LDO8 0x4E -#define MAX77620_REG_FPS_SD0 0x4F -#define MAX77620_REG_FPS_SD1 0x50 -#define MAX77620_REG_FPS_SD2 0x51 -#define MAX77620_REG_FPS_SD3 0x52 -#define MAX77620_REG_FPS_SD4 0x53 -#define MAX77620_REG_FPS_NONE 0 - -#define MAX77620_FPS_SRC_MASK 0xC0 -#define MAX77620_FPS_SRC_SHIFT 6 -#define MAX77620_FPS_PU_PERIOD_MASK 0x38 -#define MAX77620_FPS_PU_PERIOD_SHIFT 3 -#define MAX77620_FPS_PD_PERIOD_MASK 0x07 -#define MAX77620_FPS_PD_PERIOD_SHIFT 0 -#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 -#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 -#define MAX77620_FPS_EN_SRC_MASK 0x06 -#define MAX77620_FPS_EN_SRC_SHIFT 1 -#define MAX77620_FPS_ENFPS_SW_MASK 0x01 -#define MAX77620_FPS_ENFPS_SW 0x01 - -/* Minimum and maximum FPS period time (in microseconds) are - * different for MAX77620 and Max20024. - */ -#define MAX77620_FPS_PERIOD_MIN_US 40 -#define MAX20024_FPS_PERIOD_MIN_US 20 - -#define MAX77620_FPS_PERIOD_MAX_US 2560 -#define MAX20024_FPS_PERIOD_MAX_US 5120 - -#define MAX77620_REG_FPS_GPIO1 0x54 -#define MAX77620_REG_FPS_GPIO2 0x55 -#define MAX77620_REG_FPS_GPIO3 0x56 -#define MAX77620_REG_FPS_RSO 0x57 -#define MAX77620_REG_CID0 0x58 -#define MAX77620_REG_CID1 0x59 -#define MAX77620_REG_CID2 0x5A -#define MAX77620_REG_CID3 0x5B -#define MAX77620_REG_CID4 0x5C -#define MAX77620_REG_CID5 0x5D - -#define MAX77620_REG_DVSSD4 0x5E -#define MAX20024_REG_MAX_ADD 0x70 - -#define MAX77620_CID_DIDM_MASK 0xF0 -#define MAX77620_CID_DIDM_SHIFT 4 - -/* CNCG2SD */ -#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) -#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) - -/* Device Identification Metal */ -#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) -/* Device Indentification OTP */ -#define MAX77620_CID5_DIDO(n) ((n) & 0xF) - -/* SD CNFG1 */ -#define MAX77620_SD_SR_MASK 0xC0 -#define MAX77620_SD_SR_SHIFT 6 -#define MAX77620_SD_POWER_MODE_MASK 0x30 -#define MAX77620_SD_POWER_MODE_SHIFT 4 -#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) -#define MAX77620_SD_CFG1_ADE_DISABLE 0 -#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) -#define MAX77620_SD_FPWM_MASK 0x04 -#define MAX77620_SD_FPWM_SHIFT 2 -#define MAX77620_SD_FSRADE_MASK 0x01 -#define MAX77620_SD_FSRADE_SHIFT 0 -#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) -#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 -#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) -#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) -#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) -#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 -#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) - -/* LDO_CNFG2 */ -#define MAX77620_LDO_POWER_MODE_MASK 0xC0 -#define MAX77620_LDO_POWER_MODE_SHIFT 6 -#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_ENABLE (1 << 1) -#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) -#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) -#define MAX77620_LDO_CFG2_SS_SLOW 0 - -#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) -#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) -#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) -#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) -#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) -#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) -#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) - -#define MAX77620_IRQ_LBM_MASK (1 << 3) -#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) -#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) - -#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) -#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) -#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) -#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) -#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) -#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) - -#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) - -#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) -#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 -#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 -#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) -#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) -#define MAX20024_ONOFFCNFG1_CLRSE 0x18 - -#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) -#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) -#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) -#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) -#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) - -#define MAX77620_GLBLM_MASK (1 << 0) - -#define MAX77620_WDTC_MASK 0x3 -#define MAX77620_WDTOFFC (1 << 4) -#define MAX77620_WDTSLPC (1 << 3) -#define MAX77620_WDTEN (1 << 2) - -#define MAX77620_TWD_MASK 0x3 -#define MAX77620_TWD_2s 0x0 -#define MAX77620_TWD_16s 0x1 -#define MAX77620_TWD_64s 0x2 -#define MAX77620_TWD_128s 0x3 - -#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_LBRSTEN (1 << 0) - -/* CNFG BBC registers */ -#define MAX77620_CNFGBBC_ENABLE (1 << 0) -#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 -#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 -#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 -#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 -#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) -#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 -#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 - -#define MAX77620_FPS_COUNT 3 - -/* Interrupts */ -enum { - MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ - MAX77620_IRQ_TOP_SD, /* SD power fail */ - MAX77620_IRQ_TOP_LDO, /* LDO power fail */ - MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ - MAX77620_IRQ_TOP_RTC, /* RTC */ - MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ - MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ - MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ - MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ - MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ -}; - -/* GPIOs */ -enum { - MAX77620_GPIO0, - MAX77620_GPIO1, - MAX77620_GPIO2, - MAX77620_GPIO3, - MAX77620_GPIO4, - MAX77620_GPIO5, - MAX77620_GPIO6, - MAX77620_GPIO7, - MAX77620_GPIO_NR, -}; - -/* FPS Source */ -enum max77620_fps_src { - MAX77620_FPS_SRC_0, - MAX77620_FPS_SRC_1, - MAX77620_FPS_SRC_2, - MAX77620_FPS_SRC_NONE, - MAX77620_FPS_SRC_DEF, -}; - -enum max77620_chip_id { - MAX77620, - MAX20024, -}; - -#endif /* _MFD_MAX77620_H_ */ diff --git a/exosphere/rebootstub/src/shutdown.c b/exosphere/rebootstub/src/shutdown.c deleted file mode 100644 index 49353eadb..000000000 --- a/exosphere/rebootstub/src/shutdown.c +++ /dev/null @@ -1,30 +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 "utils.h" -#include "i2c.h" -#include "timer.h" - -void do_shutdown(void) { - /* Initialize i2c. */ - i2c_init(); - - /* Stop alarm, shutdown. */ - i2c_stop_rtc_alarm(); - i2c_send_shutdown_cmd(); - - while (true) { } -} \ No newline at end of file diff --git a/exosphere/rebootstub/src/start.s b/exosphere/rebootstub/src/start.s deleted file mode 100644 index e32319534..000000000 --- a/exosphere/rebootstub/src/start.s +++ /dev/null @@ -1,50 +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/>. - */ - -.section .text.start -.align 4 -.global _start -_start: - ldr r0, reboot_type - cmp r0, #0x0 - beq do_shutdown - b jump_to_reboot_payload -reboot_type: -.word 0x00000001 - -.global jump_to_reboot_payload -.type jump_to_reboot_payload, %function -jump_to_reboot_payload: - @ clear all registers - ldr r0, =0x52425430 @ RBT0 - mov r1, #0x0 - mov r2, #0x0 - mov r3, #0x0 - mov r4, #0x0 - mov r5, #0x0 - mov r6, #0x0 - mov r7, #0x0 - mov r8, #0x0 - mov r9, #0x0 - mov r10, #0x0 - mov r11, #0x0 - mov r12, #0x0 - mov lr, #0x0 - ldr sp, =0x40010000 - ldr pc, =0x40010000 - - - diff --git a/exosphere/rebootstub/src/timer.h b/exosphere/rebootstub/src/timer.h deleted file mode 100644 index 334bff1a8..000000000 --- a/exosphere/rebootstub/src/timer.h +++ /dev/null @@ -1,33 +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_REBOOTSTUB_TIMER_H -#define EXOSPHERE_REBOOTSTUB_TIMER_H - -#include "utils.h" - -#define TIMERUS_CNTR_1US_0 MAKE_REG32(0x60005010) - -static inline void timer_wait(uint32_t microseconds) { - uint32_t old_time = TIMERUS_CNTR_1US_0; - while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { - /* Spin-lock. */ - } -} - -void spinlock_wait(uint32_t count); - -#endif diff --git a/exosphere/rebootstub/src/utils.h b/exosphere/rebootstub/src/utils.h deleted file mode 100644 index eae0a6d7a..000000000 --- a/exosphere/rebootstub/src/utils.h +++ /dev/null @@ -1,38 +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_REBOOTSTUB_UTILS_H -#define EXOSPHERE_REBOOTSTUB_UTILS_H - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#define BIT(n) (1u << (n)) -#define BITL(n) (1ull << (n)) -#define MASK(n) (BIT(n) - 1) -#define MASKL(n) (BITL(n) - 1) -#define MASK2(a,b) (MASK(a) & ~MASK(b)) -#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) - -#define MAKE_REG32(a) (*(volatile uint32_t *)(a)) - -#define ALIGN(m) __attribute__((aligned(m))) -#define PACKED __attribute__((packed)) - -#define ALINLINE __attribute__((always_inline)) - -#endif diff --git a/exosphere/sc7fw/Makefile b/exosphere/sc7fw/Makefile deleted file mode 100644 index ff265beef..000000000 --- a/exosphere/sc7fw/Makefile +++ /dev/null @@ -1,154 +0,0 @@ -#--------------------------------------------------------------------------------- -.SUFFIXES: -#--------------------------------------------------------------------------------- - -ifeq ($(strip $(DEVKITARM)),) -$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") -endif - -TOPDIR ?= $(CURDIR) -include $(DEVKITARM)/base_rules - -#--------------------------------------------------------------------------------- -# 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 -DATA := data -INCLUDES := include - -#--------------------------------------------------------------------------------- -# options for code generation -#--------------------------------------------------------------------------------- -ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork - -CFLAGS := \ - -g \ - -Os \ - -ffunction-sections \ - -fdata-sections \ - -fomit-frame-pointer \ - -fno-inline \ - -std=gnu11 \ - -Werror \ - -Wall \ - $(ARCH) $(DEFINES) - -CFLAGS += $(INCLUDE) -D__BPMP__ - -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 - -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) - -LIBS := - -#--------------------------------------------------------------------------------- -# list of directories containing libraries, this must be the top level containing -# include and lib -#--------------------------------------------------------------------------------- -LIBDIRS := - - -#--------------------------------------------------------------------------------- -# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -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)/*.*))) - -#--------------------------------------------------------------------------------- -# 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) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) - -$(BUILD): - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -#--------------------------------------------------------------------------------- -clean: - @echo 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: $(OFILES) - @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 -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere/sc7fw/linker.ld b/exosphere/sc7fw/linker.ld deleted file mode 100644 index fd4272593..000000000 --- a/exosphere/sc7fw/linker.ld +++ /dev/null @@ -1,21 +0,0 @@ -OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") -OUTPUT_ARCH(arm) - -ENTRY(_start) -SECTIONS -{ - . = 0x40003000; - - __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(.); - - __stack_top__ = 0x40005000; - __stack_bottom__ = 0x40004000; -} \ No newline at end of file diff --git a/exosphere/sc7fw/linker.specs b/exosphere/sc7fw/linker.specs deleted file mode 100644 index 300990418..000000000 --- a/exosphere/sc7fw/linker.specs +++ /dev/null @@ -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 diff --git a/exosphere/sc7fw/src/emc.c b/exosphere/sc7fw/src/emc.c deleted file mode 100644 index fa92a5f46..000000000 --- a/exosphere/sc7fw/src/emc.c +++ /dev/null @@ -1,73 +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 "utils.h" -#include "sc7.h" -#include "emc.h" -#include "pmc.h" -#include "timer.h" - -static void emc_trigger_timing_update(void) { - EMC_TIMING_CONTROL_0 = 1; - while (EMC_EMC_STATUS_0 & 0x800000) { - /* Wait until TIMING_UPDATE_STALLED is unset. */ - } -} - -/* Puts DRAM into self refresh mode. */ -void emc_put_dram_in_self_refresh_mode(void) { - /* Verify CH1_ENABLE [PMC]. */ - if (!(EMC_FBIO_CFG7_0 & 4)) { - reboot(); - } - - /* Clear config. */ - EMC_CFG_0 = 0; - emc_trigger_timing_update(); - timer_wait(5); - - /* Set calibration intervals. */ - EMC_ZCAL_INTERVAL_0 = 0; - EMC_AUTO_CAL_CONFIG_0 = 0x600; /* AUTO_CAL_MEASURE_STALL | AUTO_CAL_UPDATE_STALL */ - - /* If EMC0 mirror is set, clear digital DLL. */ - if (EMC0_CFG_DIG_DLL_0 & 1) { - EMC_CFG_DIG_DLL_0 &= 0xFFFFFFFE; - emc_trigger_timing_update(); - while (EMC0_CFG_DIG_DLL_0 & 1) { /* Wait for EMC0 to clear. */ } - while (EMC1_CFG_DIG_DLL_0 & 1) { /* Wait for EMC1 to clear. */ } - } else { - emc_trigger_timing_update(); - } - - /* Stall all transactions to DRAM. */ - EMC_REQ_CTRL_0 = 3; /* STALL_ALL_WRITES | STALL_ALL_READS. */ - while (!(EMC0_EMC_STATUS_0 & 4)) { /* Wait for NO_OUTSTANDING_TRANSACTIONS for EMC0. */ } - while (!(EMC1_EMC_STATUS_0 & 4)) { /* Wait for NO_OUTSTANDING_TRANSACTIONS for EMC1. */ } - - /* Enable Self-Refresh Mode. */ - EMC_SELF_REF_0 |= 1; - - - /* Wait until we see the right devices in self refresh mode. */ - uint32_t num_populated_devices = 1; - if (EMC_ADR_CFG_0) { - num_populated_devices = 3; - } - - while (((EMC0_EMC_STATUS_0 >> 8) & 3) != num_populated_devices) { /* Wait for EMC0 DRAM_IN_SELF_REFRESH to be correct. */ } - while (((EMC1_EMC_STATUS_0 >> 8) & 3) != num_populated_devices) { /* Wait for EMC1 DRAM_IN_SELF_REFRESH to be correct. */ } -} diff --git a/exosphere/sc7fw/src/emc.h b/exosphere/sc7fw/src/emc.h deleted file mode 100644 index cc9845221..000000000 --- a/exosphere/sc7fw/src/emc.h +++ /dev/null @@ -1,67 +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_BPMPFW_EMC_H -#define EXOSPHERE_BPMPFW_EMC_H - -#include "utils.h" - -#define EMC_BASE (0x7001B000) - -#define EMC0_BASE (0x7001E000) -#define EMC1_BASE (0x7001F000) - - -#define MAKE_EMC_REG(ofs) (MAKE_REG32(EMC_BASE + ofs)) - -#define MAKE_EMC0_REG(ofs) (MAKE_REG32(EMC0_BASE + ofs)) -#define MAKE_EMC1_REG(ofs) (MAKE_REG32(EMC1_BASE + ofs)) - -#define EMC_CFG_0 MAKE_EMC_REG(0x00C) - -#define EMC_ADR_CFG_0 MAKE_EMC_REG(0x10) - -#define EMC_TIMING_CONTROL_0 MAKE_EMC_REG(0x028) - -#define EMC_SELF_REF_0 MAKE_EMC_REG(0x0E0) - -#define EMC_MRW_0 MAKE_EMC_REG(0x0E8) - -#define EMC_FBIO_CFG5_0 MAKE_EMC_REG(0x104) - -#define EMC_MRW3_0 MAKE_EMC_REG(0x138) - -#define EMC_AUTO_CAL_CONFIG_0 MAKE_EMC_REG(0x2A4) - -#define EMC_REQ_CTRL_0 MAKE_EMC_REG(0x2B0) - -#define EMC_EMC_STATUS_0 MAKE_EMC_REG(0x2B4) -#define EMC0_EMC_STATUS_0 MAKE_EMC0_REG(0x2B4) -#define EMC1_EMC_STATUS_0 MAKE_EMC1_REG(0x2B4) - -#define EMC_CFG_DIG_DLL_0 MAKE_EMC_REG(0x2BC) -#define EMC0_CFG_DIG_DLL_0 MAKE_EMC0_REG(0x2BC) -#define EMC1_CFG_DIG_DLL_0 MAKE_EMC1_REG(0x2BC) - -#define EMC_ZCAL_INTERVAL_0 MAKE_EMC_REG(0x2E0) - -#define EMC_PMC_SCRATCH3_0 MAKE_EMC_REG(0x448) - -#define EMC_FBIO_CFG7_0 MAKE_EMC_REG(0x584) - -void emc_put_dram_in_self_refresh_mode(void); - -#endif diff --git a/exosphere/sc7fw/src/i2c.c b/exosphere/sc7fw/src/i2c.c deleted file mode 100644 index a938936a0..000000000 --- a/exosphere/sc7fw/src/i2c.c +++ /dev/null @@ -1,104 +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 "i2c.h" -#include "timer.h" - -/* Prototypes for internal commands. */ -void i2c_load_config(void); -int i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes); -int i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b); - -/* Load hardware config for I2C4. */ -void i2c_load_config(void) { - /* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */ - I2C_I2C_CONFIG_LOAD_0 = 0x25; - - /* Wait a bit for master config to be loaded. */ - for (unsigned int i = 0; i < 20; i++) { - timer_wait(1); - if (!(I2C_I2C_CONFIG_LOAD_0 & 1)) { - break; - } - } -} - -/* Initialize I2C4. */ -void i2c_init(void) { - /* Setup divisor, and clear the bus. */ - I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001; - I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003; - - /* Load hardware configuration. */ - i2c_load_config(); - - /* Wait a while until BUS_CLEAR_DONE is set. */ - for (unsigned int i = 0; i < 10; i++) { - timer_wait(20000); - if (I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) { - break; - } - } - - /* Read the BUS_CLEAR_STATUS. Result doesn't matter. */ - I2C_I2C_BUS_CLEAR_STATUS_0; - - /* Read and set the Interrupt Status. */ - uint32_t int_status = I2C_INTERRUPT_STATUS_REGISTER_0; - I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; -} - -/* Writes a value to an i2c device. */ -int i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes) { - if (num_bytes > 4) { - return 0; - } - - /* Set device for 7-bit mode. */ - I2C_I2C_CMD_ADDR0_0 = device << 1; - - /* Load in data to write. */ - I2C_I2C_CMD_DATA1_0 = val; - - /* Set config with LENGTH = num_bytes, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2800; - - i2c_load_config(); - - /* Config |= SEND; */ - I2C_I2C_CNFG_0 |= 0x200; - - - while (I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */ - return (I2C_I2C_STATUS_0 & 0xF) == 0; -} - -/* Writes a byte val to reg for given device. */ -int i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b) { - uint32_t val = (reg) | (b << 8); - /* Write 1 byte (reg) + 1 byte (value) */ - return i2c_write(device, val, 2); -} - -/* Actually reset device 27. This might turn off the screen? */ -int i2c_send_reset_cmd(void) { - /* Write 00 to Device 27 Reg 00. */ - return i2c_send_byte_command(27, 0, 0); -} diff --git a/exosphere/sc7fw/src/i2c.h b/exosphere/sc7fw/src/i2c.h deleted file mode 100644 index 608069255..000000000 --- a/exosphere/sc7fw/src/i2c.h +++ /dev/null @@ -1,50 +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_BPMPFW_I2C_H -#define EXOSPHERE_BPMPFW_I2C_H - -#include "utils.h" - -/* I2C_BASE = I2C4. */ -#define I2C_BASE (0x7000D000) - -#define MAKE_I2C_REG(ofs) (MAKE_REG32(I2C_BASE + ofs)) - -#define I2C_I2C_CNFG_0 MAKE_I2C_REG(0x000) - -#define I2C_I2C_CMD_ADDR0_0 MAKE_I2C_REG(0x004) - -#define I2C_I2C_CMD_DATA1_0 MAKE_I2C_REG(0x00C) - -#define I2C_I2C_STATUS_0 MAKE_I2C_REG(0x01C) - -#define I2C_INTERRUPT_STATUS_REGISTER_0 MAKE_I2C_REG(0x068) - -#define I2C_I2C_CLK_DIVISOR_REGISTER_0 MAKE_I2C_REG(0x06C) - -#define I2C_I2C_BUS_CLEAR_CONFIG_0 MAKE_I2C_REG(0x084) - -#define I2C_I2C_BUS_CLEAR_STATUS_0 MAKE_I2C_REG(0x088) - - -#define I2C_I2C_CONFIG_LOAD_0 MAKE_I2C_REG(0x08C) - -void i2c_init(void); - -int i2c_send_reset_cmd(void); - -#endif diff --git a/exosphere/sc7fw/src/pmc.h b/exosphere/sc7fw/src/pmc.h deleted file mode 100644 index 21224fa69..000000000 --- a/exosphere/sc7fw/src/pmc.h +++ /dev/null @@ -1,53 +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_BPMPFW_PMC_H -#define EXOSPHERE_BPMPFW_PMC_H - -#include "utils.h" - -#define PMC_BASE (0x7000E400) - -#define MAKE_PMC_REG(ofs) (MAKE_REG32(PMC_BASE + ofs)) - -#define APBDEV_PMC_CNTRL_0 MAKE_PMC_REG(0x000) - -#define APBDEV_PMC_DPD_SAMPLE_0 MAKE_PMC_REG(0x020) - -#define APBDEV_PMC_DPD_ENABLE_0 MAKE_PMC_REG(0x024) - -#define APBDEV_PMC_CLAMP_STATUS_0 MAKE_PMC_REG(0x02C) - -#define APBDEV_PMC_PWRGATE_STATUS_0 MAKE_PMC_REG(0x038) - -#define APBDEV_PMC_SCRATCH12_0 MAKE_PMC_REG(0x080) -#define APBDEV_PMC_SCRATCH13_0 MAKE_PMC_REG(0x084) -#define APBDEV_PMC_SCRATCH18_0 MAKE_PMC_REG(0x098) - - -#define APBDEV_PMC_WEAK_BIAS_0 MAKE_PMC_REG(0x2C8) - -#define APBDEV_PMC_IO_DPD3_REQ_0 MAKE_PMC_REG(0x45C) -#define APBDEV_PMC_IO_DPD3_STATUS_0 MAKE_PMC_REG(0x460) - -#define APBDEV_PMC_IO_DPD4_REQ_0 MAKE_PMC_REG(0x464) -#define APBDEV_PMC_IO_DPD4_STATUS_0 MAKE_PMC_REG(0x468) - -#define APBDEV_PMC_SET_SW_CLAMP_0 MAKE_PMC_REG(0x47C) - -#define APBDEV_PMC_DDR_CNTRL_0 MAKE_PMC_REG(0x4E4) - -#endif diff --git a/exosphere/sc7fw/src/sc7.c b/exosphere/sc7fw/src/sc7.c deleted file mode 100644 index 67cef4328..000000000 --- a/exosphere/sc7fw/src/sc7.c +++ /dev/null @@ -1,120 +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 "utils.h" -#include "sc7.h" -#include "i2c.h" -#include "pmc.h" -#include "emc.h" -#include "timer.h" - -#define CACHE_CTRL MAKE_REG32(0x50040000) - -#define PRI_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004038) -#define SEC_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004138) -#define TRI_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004238) -#define QUAD_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004338) -#define PENTA_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004438) -#define HEXA_ICTLR_COP_IER_CLR_0 MAKE_REG32(0x60004538) - -void reboot(void) { - /* Write MAIN_RST */ - APBDEV_PMC_CNTRL_0 = 0x10; - while (true) { - /* Wait for reboot. */ - } -} - - -static void set_pmc_dpd_io_pads(void) { - /* Read val from EMC_PMC scratch, configure accordingly. */ - uint32_t emc_pmc_val = EMC_PMC_SCRATCH3_0; - APBDEV_PMC_DDR_CNTRL_0 = emc_pmc_val & 0x7FFFF; - if (emc_pmc_val & 0x40000000) { - APBDEV_PMC_WEAK_BIAS_0 = 0x7FFF0000; - } - /* Request to put pads in Deep Power Down. */ - APBDEV_PMC_IO_DPD3_REQ_0 = 0x8FFFFFFF; - while (APBDEV_PMC_IO_DPD3_STATUS_0 != 0xFFFFFFF) { /* Wait a while. */ } - spinlock_wait(32); - APBDEV_PMC_IO_DPD4_REQ_0 = 0x8FFFFFFF; - while (APBDEV_PMC_IO_DPD4_STATUS_0 != 0xFFF1FFF) { /* Wait a while. */ } - spinlock_wait(32); -} - -void sc7_entry_main(void) { - /* Disable the BPMP Cache. */ - CACHE_CTRL |= 0xC00; - - /* Wait until the CPU Rail is turned off. */ - while (APBDEV_PMC_PWRGATE_STATUS_0 & 1) { /* Wait for TrustZone to finish. */ } - - /* Clamp the CPU Rail. */ - APBDEV_PMC_SET_SW_CLAMP_0 |= 0x1; - while (!(APBDEV_PMC_CLAMP_STATUS_0 & 1)) { /* Wait for CPU Rail to be clamped. */ } - - /* Waste some time. */ - spinlock_wait(10); - - /* Reset device 27 over I2C, then wait a while. */ - i2c_init(); - i2c_send_reset_cmd(); - timer_wait(700); - - /* Clear Interrupt Enable for BPMP in all ICTLRs. */ - PRI_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - SEC_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - TRI_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - QUAD_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - PENTA_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - HEXA_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF; - - /* Write EMC's DRAM op into PMC scratch. */ - if ((EMC_FBIO_CFG5_0 & 3) != 1) { - /* If DRAM_TYPE != LPDDR4, something's gone wrong. Reboot. */ - reboot(); - } - /* Write MRW3_OP into scratch. */ - APBDEV_PMC_SCRATCH18_0 = (APBDEV_PMC_SCRATCH18_0 & 0xFFFFFF3F) | (EMC_MRW3_0 & 0xC0); - uint32_t mrw3_op = ((EMC_MRW3_0 & 0xC0) << 8) | (EMC_MRW3_0 & 0xC0); - APBDEV_PMC_SCRATCH12_0 = (APBDEV_PMC_SCRATCH12_0 & 0xFFFF3F3F) | mrw3_op; - APBDEV_PMC_SCRATCH13_0 = (APBDEV_PMC_SCRATCH13_0 & 0xFFFF3F3F) | mrw3_op; - - /* Ready DRAM for deep sleep. */ - emc_put_dram_in_self_refresh_mode(); - - /* Setup LPDDR MRW based on device config. */ - EMC_MRW_0 = 0x88110000; - if (EMC_ADR_CFG_0 & 1) { - EMC_MRW_0 = 0x48110000; - } - - /* Put IO pads in Deep Power Down. */ - set_pmc_dpd_io_pads(); - - /* Enable pad sampling during deep sleep. */ - APBDEV_PMC_DPD_SAMPLE_0 |= 1; - - /* Waste some more time. */ - spinlock_wait(0x128); - - /* Enter deep sleep. */ - APBDEV_PMC_DPD_ENABLE_0 |= 1; - - while (true) { /* Wait until we're asleep. */ } -} - - diff --git a/exosphere/sc7fw/src/sc7.h b/exosphere/sc7fw/src/sc7.h deleted file mode 100644 index 03544c453..000000000 --- a/exosphere/sc7fw/src/sc7.h +++ /dev/null @@ -1,26 +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_BPMPFW_SC7_H -#define EXOSPHERE_BPMPFW_SC7_H - -#include "utils.h" - -void sc7_entry_main(void); - -void reboot(void); - -#endif diff --git a/exosphere/sc7fw/src/start.s b/exosphere/sc7fw/src/start.s deleted file mode 100644 index f4f639daf..000000000 --- a/exosphere/sc7fw/src/start.s +++ /dev/null @@ -1,40 +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/>. - */ - -.section .text.start -.align 4 -.global _start -_start: - b crt0 -.global _reboot - b reboot - -.global crt0 -.type crt0, %function -crt0: - @ setup to call sc7_entry_main - msr cpsr_cxsf, #0xD3 - ldr sp, =__stack_top__ - ldr lr, =reboot - b sc7_entry_main - - -.global spinlock_wait -.type spinlock_wait, %function -spinlock_wait: - subs r0, r0, #1 - bgt spinlock_wait - bx lr diff --git a/exosphere/sc7fw/src/timer.h b/exosphere/sc7fw/src/timer.h deleted file mode 100644 index 023bd443e..000000000 --- a/exosphere/sc7fw/src/timer.h +++ /dev/null @@ -1,33 +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_BPMPFW_TIMER_H -#define EXOSPHERE_BPMPFW_TIMER_H - -#include "utils.h" - -#define TIMERUS_CNTR_1US_0 MAKE_REG32(0x60005010) - -static inline void timer_wait(uint32_t microseconds) { - uint32_t old_time = TIMERUS_CNTR_1US_0; - while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { - /* Spin-lock. */ - } -} - -void spinlock_wait(uint32_t count); - -#endif diff --git a/exosphere/sc7fw/src/utils.h b/exosphere/sc7fw/src/utils.h deleted file mode 100644 index 8e96a937d..000000000 --- a/exosphere/sc7fw/src/utils.h +++ /dev/null @@ -1,38 +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_BPMPFW_UTILS_H -#define EXOSPHERE_BPMPFW_UTILS_H - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#define BIT(n) (1u << (n)) -#define BITL(n) (1ull << (n)) -#define MASK(n) (BIT(n) - 1) -#define MASKL(n) (BITL(n) - 1) -#define MASK2(a,b) (MASK(a) & ~MASK(b)) -#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) - -#define MAKE_REG32(a) (*(volatile uint32_t *)(a)) - -#define ALIGN(m) __attribute__((aligned(m))) -#define PACKED __attribute__((packed)) - -#define ALINLINE __attribute__((always_inline)) - -#endif diff --git a/exosphere/src/actmon.c b/exosphere/src/actmon.c deleted file mode 100644 index f638453fd..000000000 --- a/exosphere/src/actmon.c +++ /dev/null @@ -1,40 +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 "actmon.h" - -static void (*g_actmon_callback)(void) = NULL; - -void actmon_set_callback(void (*callback)(void)) { - g_actmon_callback = callback; -} - -void actmon_on_bpmp_wakeup(void) { - /* This gets set as the actmon interrupt handler on 4.x. */ - panic(0xF0A00036); -} - -void actmon_interrupt_handler(void) { - ACTMON_COP_CTRL_0 = 0; - ACTMON_COP_INTR_STATUS_0 = ACTMON_COP_INTR_STATUS_0; - if (g_actmon_callback != NULL) { - g_actmon_callback(); - g_actmon_callback = NULL; - } -} \ No newline at end of file diff --git a/exosphere/src/actmon.h b/exosphere/src/actmon.h deleted file mode 100644 index 37038e465..000000000 --- a/exosphere/src/actmon.h +++ /dev/null @@ -1,41 +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_ACTIVITY_MONITOR_H -#define EXOSPHERE_ACTIVITY_MONITOR_H - -#include "sysreg.h" - -/* Exosphere Driver for the Tegra X1 Activity Monitor. */ - -/* NOTE: ACTMON registers lie in the SYSREG region! */ -#define ACTMON_BASE (SYSREG_BASE + 0x800) - -#define MAKE_ACTMON_REG(n) MAKE_REG32(ACTMON_BASE + n) - -#define ACTMON_GLB_STATUS_0 MAKE_ACTMON_REG(0x000) -#define ACTMON_GLB_PERIOD_CTRL_0 MAKE_ACTMON_REG(0x004) -#define ACTMON_COP_CTRL_0 MAKE_ACTMON_REG(0x0C0) -#define ACTMON_COP_UPPER_WMARK_0 MAKE_ACTMON_REG(0x0C4) -#define ACTMON_COP_INTR_STATUS_0 MAKE_ACTMON_REG(0x0E4) - -void actmon_interrupt_handler(void); - -void actmon_on_bpmp_wakeup(void); - -void actmon_set_callback(void (*callback)(void)); - -#endif diff --git a/exosphere/src/arm.h b/exosphere/src/arm.h deleted file mode 100644 index 565d93acc..000000000 --- a/exosphere/src/arm.h +++ /dev/null @@ -1,40 +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_ARM_H -#define EXOSPHERE_ARM_H - -#include <stdint.h> - -void tlb_invalidate_all(void); -void tlb_invalidate_all_inner_shareable(void); - -void tlb_invalidate_page(const volatile void *page); -void tlb_invalidate_page_inner_shareable(const void *page); - -void flush_dcache_all(void); -void invalidate_dcache_all(void); - -void flush_dcache_range(const void *start, const void *end); -void invalidate_dcache_range(const void *start, const void *end); - -void invalidate_icache_all_inner_shareable(void); -void invalidate_icache_all(void); - -void finalize_powerdown(void); -void call_with_stack_pointer(uintptr_t stack_pointer, void (*function)(void)); - -#endif diff --git a/exosphere/src/arm.s b/exosphere/src/arm.s deleted file mode 100644 index dd943d1b5..000000000 --- a/exosphere/src/arm.s +++ /dev/null @@ -1,316 +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/>. - */ - -#define cpuactlr_el1 s3_1_c15_c2_0 -#define cpuectlr_el1 s3_1_c15_c2_1 - -.section .text.tlb_invalidate_all, "ax", %progbits -.type tlb_invalidate_all, %function -.global tlb_invalidate_all -tlb_invalidate_all: - dsb sy - tlbi alle3 - dsb sy - isb - ret - -.section .text.tlb_invalidate_all_inner_shareable, "ax", %progbits -.type tlb_invalidate_all_inner_shareable, %function -.global tlb_invalidate_all_inner_shareable -tlb_invalidate_all_inner_shareable: - dsb ish - tlbi alle3is - dsb ish - isb - ret - -.section .text.tlb_invalidate_page, "ax", %progbits -.type tlb_invalidate_page, %function -.global tlb_invalidate_page -tlb_invalidate_page: - lsr x8, x0, #12 - dsb sy - tlbi vale3, x8 - dsb sy - isb - ret - -.section .text.tlb_invalidate_page_inner_shareable, "ax", %progbits -.type tlb_invalidate_page_inner_shareable, %function -.global tlb_invalidate_page_inner_shareable -tlb_invalidate_page_inner_shareable: - lsr x8, x0, #12 - dsb ish - tlbi vale3is, x8 - dsb ish - isb - ret - -/* The following functions are taken/adapted from https://github.com/u-boot/u-boot/blob/master/arch/arm/cpu/armv8/cache.S */ - -/* - * (C) Copyright 2013 - * David Feng <fenghua@phytium.com.cn> - * - * This file is based on sample code from ARMv8 ARM. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * void __asm_dcache_level(level) - * - * flush or invalidate one level cache. - * - * x0: cache level - * x1: 0 clean & invalidate, 1 invalidate only - * x2~x9: clobbered - */ -.section .warm_crt0.text.__asm_dcache_level, "ax", %progbits -.type __asm_dcache_level, %function -__asm_dcache_level: - lsl x12, x0, #1 - msr csselr_el1, x12 /* select cache level */ - isb /* sync change of cssidr_el1 */ - mrs x6, ccsidr_el1 /* read the new cssidr_el1 */ - and x2, x6, #7 /* x2 <- log2(cache line size)-4 */ - add x2, x2, #4 /* x2 <- log2(cache line size) */ - mov x3, #0x3ff - and x3, x3, x6, lsr #3 /* x3 <- max number of #ways */ - clz w5, w3 /* bit position of #ways */ - mov x4, #0x7fff - and x4, x4, x6, lsr #13 /* x4 <- max number of #sets */ - /* x12 <- cache level << 1 */ - /* x2 <- line length offset */ - /* x3 <- number of cache ways - 1 */ - /* x4 <- number of cache sets - 1 */ - /* x5 <- bit position of #ways */ - -loop_set: - mov x6, x3 /* x6 <- working copy of #ways */ -loop_way: - lsl x7, x6, x5 - orr x9, x12, x7 /* map way and level to cisw value */ - lsl x7, x4, x2 - orr x9, x9, x7 /* map set number to cisw value */ - tbz w1, #0, 1f - dc isw, x9 - b 2f -1: dc cisw, x9 /* clean & invalidate by set/way */ -2: subs x6, x6, #1 /* decrement the way */ - b.ge loop_way - subs x4, x4, #1 /* decrement the set */ - b.ge loop_set - - ret - -/* - * void __asm_flush_dcache_all(int invalidate_only) - * - * x0: 0 clean & invalidate, 1 invalidate only - * - * flush or invalidate all data cache by SET/WAY. - */ -.section .warm_crt0.text.__asm_dcache_all, "ax", %progbits -.type __asm_dcache_all, %function -__asm_dcache_all: - mov x1, x0 - dsb sy - mrs x10, clidr_el1 /* read clidr_el1 */ - lsr x11, x10, #24 - and x11, x11, #0x7 /* x11 <- loc */ - cbz x11, finished /* if loc is 0, exit */ - mov x15, lr - mov x0, #0 /* start flush at cache level 0 */ - /* x0 <- cache level */ - /* x10 <- clidr_el1 */ - /* x11 <- loc */ - /* x15 <- return address */ - -loop_level: - lsl x12, x0, #1 - add x12, x12, x0 /* x0 <- tripled cache level */ - lsr x12, x10, x12 - and x12, x12, #7 /* x12 <- cache type */ - cmp x12, #2 - b.lt skip /* skip if no cache or icache */ - bl __asm_dcache_level /* x1 = 0 flush, 1 invalidate */ -skip: - add x0, x0, #1 /* increment cache level */ - cmp x11, x0 - b.gt loop_level - - mov x0, #0 - msr csselr_el1, x0 /* restore csselr_el1 */ - dsb sy - isb - mov lr, x15 - -finished: - ret - -.section .warm_crt0.text.flush_dcache_all, "ax", %progbits -.type flush_dcache_all, %function -.global flush_dcache_all -flush_dcache_all: - mov x0, #0 - b __asm_dcache_all - -.section .warm_crt0.text.invalidate_dcache_all, "ax", %progbits -.type invalidate_dcache_all, %function -.global invalidate_dcache_all -invalidate_dcache_all: - mov x0, #1 - b __asm_dcache_all - -/* - * void __asm_flush_dcache_range(start, end) (renamed -> flush_dcache_range) - * - * clean & invalidate data cache in the range - * - * x0: start address - * x1: end address - */ -.section .warm_crt0.text.flush_dcache_range, "ax", %progbits -.type flush_dcache_range, %function -.global flush_dcache_range -flush_dcache_range: - mrs x3, ctr_el0 - lsr x3, x3, #16 - and x3, x3, #0xf - mov x2, #4 - lsl x2, x2, x3 /* cache line size */ - - /* x2 <- minimal cache line size in cache system */ - sub x3, x2, #1 - bic x0, x0, x3 -1: dc civac, x0 /* clean & invalidate data or unified cache */ - add x0, x0, x2 - cmp x0, x1 - b.lo 1b - dsb sy - ret - -/* - * void __asm_invalidate_dcache_range(start, end) (-> invalidate_dcache_range) - * - * invalidate data cache in the range - * - * x0: start address - * x1: end address - */ -.section .warm_crt0.text.invalidate_dcache_range, "ax", %progbits -.type invalidate_dcache_range, %function -.global invalidate_dcache_range -invalidate_dcache_range: - mrs x3, ctr_el0 - ubfm x3, x3, #16, #19 - mov x2, #4 - lsl x2, x2, x3 /* cache line size */ - - /* x2 <- minimal cache line size in cache system */ - sub x3, x2, #1 - bic x0, x0, x3 -1: dc ivac, x0 /* invalidate data or unified cache */ - add x0, x0, x2 - cmp x0, x1 - b.lo 1b - dsb sy - ret - -/* - * void __asm_invalidate_icache_all(void) (-> invalidate_icache_inner_shareable) - * - * invalidate all icache entries. - */ -.section .warm_crt0.text.invalidate_icache_all_inner_shareable, "ax", %progbits -.type invalidate_icache_all_inner_shareable, %function -.global invalidate_icache_all_inner_shareable -invalidate_icache_all_inner_shareable: - dsb ish - isb - ic ialluis - dsb ish - isb - ret - -.section .warm_crt0.text.invalidate_icache_all, "ax", %progbits -.type invalidate_icache_all, %function -.global invalidate_icache_all -invalidate_icache_all: - dsb ish - isb - ic iallu - dsb ish - isb - ret - -/* Final steps before power down. */ -.section .text.finalize_powerdown, "ax", %progbits -.type finalize_powerdown, %function -.global finalize_powerdown -finalize_powerdown: - /* - Make all data access to Normal memory from EL0/EL1 + all Normal Memory accesses to EL0/1 stage 1 translation tables - non-cacheable for all levels, unified cache. - */ - mrs x0, sctlr_el1 - bic x0, x0, #(1 << 2) - msr sctlr_el1, x0 - isb - - /* Same as above, for EL3. */ - mrs x0, sctlr_el3 - bic x0, x0, #(1 << 2) - msr sctlr_el3, x0 - isb - - /* Disable table walk descriptor access prefetch, disable instruction prefetch, disable data prefetch. */ - mrs x0, cpuectlr_el1 - orr x0, x0, #(1 << 38) - bic x0, x0, #(3 << 35) - bic x0, x0, #(3 << 32) - msr cpuectlr_el1, x0 - isb - dsb sy - bl flush_dcache_all - - /* Disable receiving instruction cache/tbl maintenance operations. */ - mrs x0, cpuectlr_el1 - bic x0, x0, #(1 << 6) - msr cpuectlr_el1, x0 - - /* Prepare GICC */ - bl intr_prepare_gicc_for_sleep - - /* Set OS double lock */ - mrs x0, osdlr_el1 - orr x0, x0, #1 - msr osdlr_el1, x0 - - isb - dsb sy -wait_for_power_off: - wfi - b wait_for_power_off - -/* Call a function with desired stack pointer. */ -.section .text.call_with_stack_pointer, "ax", %progbits -.type call_with_stack_pointer, %function -.global call_with_stack_pointer -call_with_stack_pointer: - mov sp, x0 - br x1 diff --git a/exosphere/src/bootconfig.c b/exosphere/src/bootconfig.c deleted file mode 100644 index 5d3896f9e..000000000 --- a/exosphere/src/bootconfig.c +++ /dev/null @@ -1,164 +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 <stdbool.h> -#include <string.h> - -#include "utils.h" -#include "se.h" -#include "configitem.h" -#include "fuse.h" -#include "bootconfig.h" - -static boot_reason_t g_boot_reason = {0}; -static uint64_t g_package2_hash_for_recovery[4] = {0}; - -bool bootconfig_matches_hardware_info(void) { - uint32_t hardware_info[4]; - fuse_get_hardware_info(hardware_info); - return memcmp(LOADED_BOOTCONFIG->signed_config.hardware_info, hardware_info, sizeof(hardware_info)) == 0; -} - -void bootconfig_load_and_verify(const bootconfig_t *bootconfig) { - static const uint8_t bootconfig_modulus[RSA_2048_BYTES] = { - 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, - 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, - 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, - 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, - 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, - 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, - 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, - 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, - 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, - 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, - 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, - 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, - 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, - 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, - 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, - 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D - }; - memcpy(LOADED_BOOTCONFIG, bootconfig, sizeof(bootconfig_t)); - /* TODO: Should these restrictions be loosened for Exosphere? */ - if (configitem_is_retail() - || se_rsa2048_pss_verify(LOADED_BOOTCONFIG->signature, RSA_2048_BYTES, bootconfig_modulus, RSA_2048_BYTES, &LOADED_BOOTCONFIG->signed_config, sizeof(LOADED_BOOTCONFIG->signed_config)) != 0 - || !bootconfig_matches_hardware_info()) { - /* Clear signed config. */ - memset(&LOADED_BOOTCONFIG->signed_config, 0, sizeof(LOADED_BOOTCONFIG->signed_config)); - } -} - -void bootconfig_clear(void){ - memset(LOADED_BOOTCONFIG, 0, sizeof(bootconfig_t)); -} - -/* Actual configuration getters. */ -bool bootconfig_is_package2_plaintext(void) { - return (LOADED_BOOTCONFIG->signed_config.package2_config & 1) != 0; -} - -bool bootconfig_is_package2_unsigned(void) { - return (LOADED_BOOTCONFIG->signed_config.package2_config & 2) != 0; -} - -void bootconfig_set_package2_plaintext_and_unsigned(void) { - LOADED_BOOTCONFIG->signed_config.package2_config |= 3; -} - -bool bootconfig_disable_program_verification(void) { - return LOADED_BOOTCONFIG->signed_config.disable_program_verification != 0; -} - -bool bootconfig_is_debug_mode(void) { - return (LOADED_BOOTCONFIG->unsigned_config.data[0x10] & 2) != 0; -} - -bool bootconfig_take_extabt_serror_to_el3(void) { - return (LOADED_BOOTCONFIG->unsigned_config.data[0x10] & 6) != 6; -} - -uint64_t bootconfig_get_value_for_sysctr0(void) { - if (LOADED_BOOTCONFIG->unsigned_config.data[0x24] & 1) { - return *(volatile uint64_t *)(&(LOADED_BOOTCONFIG->unsigned_config.data[0x30])); - } else { - return 0ULL; - } -} - -uint64_t bootconfig_get_memory_arrangement(void) { - /* TODO: This function has changed pretty significantly since we implemented it. */ - /* Not relevant for retail, but we'll probably want this to be accurate sooner or later. */ - if (bootconfig_is_debug_mode()) { - if (fuse_get_dram_id() == 4) { - if (LOADED_BOOTCONFIG->unsigned_config.data[0x23]) { - return (uint64_t)(LOADED_BOOTCONFIG->unsigned_config.data[0x23]); - } else { - return 0x11ull; - } - } else { - if (LOADED_BOOTCONFIG->unsigned_config.data[0x23]) { - if ((LOADED_BOOTCONFIG->unsigned_config.data[0x23] & 0x30) == 0) { - return (uint64_t)(LOADED_BOOTCONFIG->unsigned_config.data[0x23]); - } else { - return 1ull; - } - } else { - return 1ull; - } - } - } else { - return 1ull; - } -} - -uint64_t bootconfig_get_kernel_configuration(void) { - if (bootconfig_is_debug_mode()) { - uint64_t high_val = 0; - if (fuse_get_dram_id() == 4) { - if (LOADED_BOOTCONFIG->unsigned_config.data[0x23]) { - high_val = ((uint64_t)(LOADED_BOOTCONFIG->unsigned_config.data[0x23]) >> 4) & 0x3; - } else { - high_val = 0x1; - } - } - return (high_val << 16) | (((uint64_t)(LOADED_BOOTCONFIG->unsigned_config.data[0x21])) << 8) | ((uint64_t)(LOADED_BOOTCONFIG->unsigned_config.data[0x11])); - } else { - return 0ull; - } -} - -void bootconfig_load_boot_reason(volatile boot_reason_t *boot_reason) { - g_boot_reason = *boot_reason; -} - -void bootconfig_set_package2_hash_for_recovery(const void *package2, size_t package2_size) { - se_calculate_sha256(g_package2_hash_for_recovery, package2, package2_size); -} - -void bootconfig_get_package2_hash_for_recovery(uint64_t *out_hash) { - for (unsigned int i = 0; i < 4; i++) { - out_hash[i] = g_package2_hash_for_recovery[i]; - } -} - -bool bootconfig_is_recovery_boot(void) { - return ((g_boot_reason.bootloader_attribute & 0x01) != 0); -} - -uint64_t bootconfig_get_boot_reason(void) { - return ((uint64_t)g_boot_reason.boot_reason_state << 24) | (g_boot_reason.boot_reason_value & 0xFFFFFF); -} diff --git a/exosphere/src/bootconfig.h b/exosphere/src/bootconfig.h deleted file mode 100644 index 7452a0dfb..000000000 --- a/exosphere/src/bootconfig.h +++ /dev/null @@ -1,94 +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_BOOTCONFIG_H -#define EXOSPHERE_BOOTCONFIG_H - -#include <stdbool.h> -#include <stdint.h> -#include "memory_map.h" - -/* This is kind of ConfigItem, but it's stored in BootConfig, so... */ -typedef enum { - KERNELCONFIGFLAG_INITIALIZE_MEMORY_TO_PATTERN = (1 << 0), - KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS = (1 << 1), - KERNELCONFIGFLAG_ENABLE_USER_PMU_ACCESS = (1 << 2), - KERNELCONFIGFLAG_CALL_SMC_PANIC_ON_KERNEL_ERROR = (1 << 8), -} KernelConfigFlag; - -/* This provides management for Switch BootConfig. */ - -#define LOADED_BOOTCONFIG (get_loaded_bootconfig()) - -typedef struct { - uint8_t data[0x200]; -} bootconfig_unsigned_config_t; - -typedef struct { - uint8_t _0x0[8]; - uint8_t package2_config; - uint8_t _0x9[7]; - uint8_t hardware_info[0x10]; - uint8_t disable_program_verification; - uint8_t _0x21[0xDF]; -} bootconfig_signed_config_t; - -typedef struct { - bootconfig_unsigned_config_t unsigned_config; - uint8_t signature[0x100]; - bootconfig_signed_config_t signed_config; -} bootconfig_t; - -static inline bootconfig_t *get_loaded_bootconfig(void) { - /* this is also get_exception_entry_stack_address(2) */ - return (bootconfig_t *)(TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x180); -} - -typedef struct { - uint32_t bootloader_version; - uint32_t bootloader_start_block; - uint32_t bootloader_start_page; - uint32_t bootloader_attribute; - uint32_t boot_reason_value; - uint32_t boot_reason_state; -} boot_reason_t; - -void bootconfig_load_and_verify(const bootconfig_t *bootconfig); -void bootconfig_clear(void); - -void bootconfig_load_boot_reason(volatile boot_reason_t *boot_reason); - -void bootconfig_set_package2_hash_for_recovery(const void *package2, size_t package2_size); -void bootconfig_get_package2_hash_for_recovery(uint64_t *out_hash); - -/* Actual configuration getters. */ -bool bootconfig_is_package2_plaintext(void); -bool bootconfig_is_package2_unsigned(void); -void bootconfig_set_package2_plaintext_and_unsigned(void); -bool bootconfig_disable_program_verification(void); -bool bootconfig_is_debug_mode(void); - -bool bootconfig_take_extabt_serror_to_el3(void); - -uint64_t bootconfig_get_value_for_sysctr0(void); - -uint64_t bootconfig_get_memory_arrangement(void); -uint64_t bootconfig_get_kernel_configuration(void); - -bool bootconfig_is_recovery_boot(void); -uint64_t bootconfig_get_boot_reason(void); - -#endif diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c deleted file mode 100644 index 47916e315..000000000 --- a/exosphere/src/bootup.c +++ /dev/null @@ -1,381 +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 <stdbool.h> - -#include "utils.h" -#include "bootup.h" - -#include "fuse.h" -#include "bpmp.h" -#include "flow.h" -#include "pmc.h" -#include "mc.h" -#include "car.h" -#include "se.h" -#include "masterkey.h" -#include "configitem.h" -#include "timers.h" -#include "misc.h" -#include "uart.h" -#include "bpmp.h" -#include "sysreg.h" -#include "interrupt.h" -#include "cpu_context.h" -#include "actmon.h" -#include "sysctr0.h" -#include "exocfg.h" - -#include "mmu.h" -#include "arm.h" -#include "memory_map.h" -#include "synchronization.h" - -static bool g_has_booted_up = false; - -void setup_dram_magic_numbers(void) { - /* These DRAM writes test and set values for the GPU UCODE carveout. */ - unsigned int target_fw = exosphere_get_target_firmware(); - (*(volatile uint32_t *)(0x8005FFFC)) = 0xC0EDBBCC; /* Access test value. */ - flush_dcache_range((void *)0x8005FFFC, (void *)0x80060000); - if (ATMOSPHERE_TARGET_FIRMWARE_6_0_0 <= target_fw) { - (*(volatile uint32_t *)(0x8005FF00)) = 0x00000083; /* SKU code. */ - (*(volatile uint32_t *)(0x8005FF04)) = 0x00000002; - (*(volatile uint32_t *)(0x8005FF08)) = 0x00000210; /* Tegra210 code. */ - flush_dcache_range((void *)0x8005FF00, (void *)0x8005FF0C); - } - __dsb_sy(); -} - -void bootup_misc_mmio(void) { - /* Initialize Fuse registers. */ - fuse_init(); - - /* Verify Security Engine sanity. */ - se_set_in_context_save_mode(false); - /* TODO: se_verify_keys_unreadable(); */ - se_validate_stored_vector(); - - for (unsigned int i = 0; i < KEYSLOT_SWITCH_SESSIONKEY; i++) { - clear_aes_keyslot(i); - } - - for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { - clear_rsa_keyslot(i); - } - se_initialize_rng(KEYSLOT_SWITCH_RNGKEY); - se_generate_random_key(KEYSLOT_SWITCH_SRKGENKEY, KEYSLOT_SWITCH_RNGKEY); - se_generate_srk(KEYSLOT_SWITCH_SRKGENKEY); - - if (!g_has_booted_up && (ATMOSPHERE_TARGET_FIRMWARE_6_0_0 > exosphere_get_target_firmware())) { - setup_dram_magic_numbers(); - } - - /* On 9.0.0+, Nintendo writes random values to context save scratch here, and locks the SRK scratch. */ - /* There's no real need for us to do this, so we won't. */ - - /* Mark TMR5, TMR6, TMR7, TMR8, WDT0, WDT1, WDT2 and WDT3 as secure. */ - SHARED_TIMER_SECURE_CFG_0 = 0xF1E0; - - FLOW_CTLR_BPMP_CLUSTER_CONTROL_0 = 4; /* ACTIVE_CLUSTER_LOCK. */ - FLOW_CTLR_FLOW_DBG_QUAL_0 = 0x10000000; /* Enable FIQ2CCPLEX */ - - /* Disable Deep Power Down. */ - APBDEV_PMC_DPD_ENABLE_0 = 0; - - /* Setup MC carveouts. */ - MAKE_MC_REG(MC_VIDEO_PROTECT_GPU_OVERRIDE_0) = 1; - MAKE_MC_REG(MC_VIDEO_PROTECT_GPU_OVERRIDE_1) = 0; - MAKE_MC_REG(MC_VIDEO_PROTECT_BOM) = 0; - MAKE_MC_REG(MC_VIDEO_PROTECT_SIZE_MB) = 0; - MAKE_MC_REG(MC_VIDEO_PROTECT_REG_CTRL) = 1; - MAKE_MC_REG(MC_SEC_CARVEOUT_BOM) = 0; - MAKE_MC_REG(MC_SEC_CARVEOUT_SIZE_MB) = 0; - MAKE_MC_REG(MC_SEC_CARVEOUT_REG_CTRL) = 1; - MAKE_MC_REG(MC_MTS_CARVEOUT_BOM) = 0; - MAKE_MC_REG(MC_MTS_CARVEOUT_SIZE_MB) = 0; - MAKE_MC_REG(MC_MTS_CARVEOUT_ADR_HI) = 0; - MAKE_MC_REG(MC_MTS_CARVEOUT_REG_CTRL) = 1; - MAKE_MC_REG(MC_SECURITY_CFG0) = 0; - MAKE_MC_REG(MC_SECURITY_CFG1) = 0; - MAKE_MC_REG(MC_SECURITY_CFG3) = 3; - configure_default_carveouts(); - - /* Mark registers secure world only. */ - if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 = APB_SSER0_SATA_AUX | APB_SSER0_DTV | APB_SSER0_QSPI | APB_SSER0_SATA | APB_SSER0_LA; - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 = APB_SSER1_SPI1 | APB_SSER1_SPI2 | APB_SSER1_SPI3 | APB_SSER1_SPI5 | APB_SSER1_SPI6 | APB_SSER1_I2C4 | APB_SSER1_I2C6; - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 = 1 << 4 | 1 << 5 | APB_SSER2_DDS; /* bits 4 and 5 are not labeled in 21.1.7.3 */ - } else { - /* Mark SATA_AUX, DTV, QSPI, SE, SATA, LA secure only. */ - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 = APB_SSER0_SATA_AUX | APB_SSER0_DTV | APB_SSER0_QSPI | APB_SSER0_SE | APB_SSER0_SATA | APB_SSER0_LA; - - /* By default, mark SPI1, SPI2, SPI3, SPI5, SPI6, I2C6 secure only. */ - uint32_t sec_disable_1 = APB_SSER1_SPI1 | APB_SSER1_SPI2 | APB_SSER1_SPI3 | APB_SSER1_SPI5 | APB_SSER1_SPI6 | APB_SSER1_I2C6; - /* By default, mark SDMMC3, DDS, DP2 secure only. */ - uint32_t sec_disable_2 = APB_SSER2_SDMMC3 | APB_SSER2_DDS | APB_SSER2_DP2; - uint64_t hardware_type = configitem_get_hardware_type(); - if (hardware_type != 1) { - /* Also mark I2C4 secure only, */ - sec_disable_1 |= APB_SSER1_I2C4; - } - if (hardware_type != 0 && exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - /* Starting on 4.x on non-dev units, mark UARTB, UARTC, SPI4, I2C3 secure only. */ - sec_disable_1 |= APB_SSER1_UART_B | APB_SSER1_UART_C | APB_SSER1_SPI4 | APB_SSER1_I2C3; - /* Starting on 4.x on non-dev units, mark SDMMC1 secure only. */ - sec_disable_2 |= APB_SSER2_SDMMC1; - } - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 = sec_disable_1; - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 = sec_disable_2; - } - - /* Reset Translation Enable registers. */ - MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_0) = 0xFFFFFFFF; - MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_1) = 0xFFFFFFFF; - MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_2) = 0xFFFFFFFF; - MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_3) = 0xFFFFFFFF; - MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_4) = 0xFFFFFFFF; - - /* Set SMMU ASID security registers. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0xE; - } else { - MAKE_MC_REG(MC_SMMU_ASID_SECURITY) = 0x0; - } - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_1) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_2) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_3) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_4) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_5) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_6) = 0; - MAKE_MC_REG(MC_SMMU_ASID_SECURITY_7) = 0; - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAKE_MC_REG(MC_SMMU_PTB_ASID) = 0; - } - MAKE_MC_REG(MC_SMMU_PTB_DATA) = 0; - MAKE_MC_REG(MC_SMMU_TLB_CONFIG) = 0x30000030; - MAKE_MC_REG(MC_SMMU_PTC_CONFIG) = 0x2800003F; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_PTC_FLUSH) = 0; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_TLB_FLUSH) = 0; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_CONFIG) = 1; /* Enable SMMU. */ - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - - /* Clear RESET Vector, setup CPU Secure Boot RESET Vectors. */ - uint32_t reset_vec; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - reset_vec = TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN); - } else { - reset_vec = TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN); - } - EVP_CPU_RESET_VECTOR_0 = 0; - SB_AA64_RESET_LOW_0 = reset_vec | 1; - SB_AA64_RESET_HIGH_0 = 0; - - /* Lock Non-Secure writes to Secure Boot RESET Vector. */ - SB_CSR_0 = 2; - - /* Setup PMC Secure Scratch RESET Vector for warmboot. */ - APBDEV_PMC_SECURE_SCRATCH34_0 = reset_vec; - APBDEV_PMC_SECURE_SCRATCH35_0 = 0; - APBDEV_PMC_SEC_DISABLE3_0 = 0x500000; - - /* Setup FIQs. */ - - /* And assign "se_operation_completed" to Interrupt 0x5A. */ - intr_set_priority(INTERRUPT_ID_SECURITY_ENGINE, 0); - intr_set_group(INTERRUPT_ID_SECURITY_ENGINE, 0); - intr_set_enabled(INTERRUPT_ID_SECURITY_ENGINE, 1); - intr_set_cpu_mask(INTERRUPT_ID_SECURITY_ENGINE, 8); - intr_set_edge_level(INTERRUPT_ID_SECURITY_ENGINE, 0); - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - intr_set_priority(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); - intr_set_group(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); - intr_set_enabled(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 1); - intr_set_cpu_mask(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 8); - intr_set_edge_level(INTERRUPT_ID_ACTIVITY_MONITOR_4X, 0); - } - - if (!g_has_booted_up) { - /* N doesn't do this, but we should for compatibility. */ - uart_config(UART_A); - clkrst_reboot(CARDEVICE_UARTA); - uart_init(UART_A, 115200); - - intr_register_handler(INTERRUPT_ID_SECURITY_ENGINE, se_operation_completed); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - intr_register_handler(INTERRUPT_ID_ACTIVITY_MONITOR_4X, actmon_interrupt_handler); - } - for (unsigned int core = 1; core < NUM_CPU_CORES; core++) { - set_core_is_active(core, false); - } - g_has_booted_up = true; - } else if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - /* Disable AHB redirect. */ - MAKE_MC_REG(MC_IRAM_BOM) = 0xFFFFF000; - MAKE_MC_REG(MC_IRAM_TOM) = 0; - MAKE_MC_REG(MC_IRAM_REG_CTRL) |= 1; - CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 &= 0xFFF7FFFF; - } -} - -void setup_4x_mmio(void) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - configure_gpu_ucode_carveout(); - } - - /* Disable AHB redirect. */ - MAKE_MC_REG(MC_IRAM_BOM) = 0xFFFFF000; - MAKE_MC_REG(MC_IRAM_TOM) = 0; - MAKE_MC_REG(MC_IRAM_REG_CTRL) |= 1; - CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 &= 0xFFF7FFFF; - - /* TODO: What are these PMC scratch writes? */ - APBDEV_PMC_SECURE_SCRATCH51_0 = (APBDEV_PMC_SECURE_SCRATCH51_0 & 0xFFFF8000) | 0x4000; - APBDEV_PMC_SECURE_SCRATCH16_0 &= 0x3FFFFFFF; - APBDEV_PMC_SECURE_SCRATCH55_0 = (APBDEV_PMC_SECURE_SCRATCH55_0 & 0xFF000FFF) | 0x1000; - APBDEV_PMC_SECURE_SCRATCH74_0 = 0x40008000; - APBDEV_PMC_SECURE_SCRATCH75_0 = 0x40000; - APBDEV_PMC_SECURE_SCRATCH76_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH77_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH78_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH99_0 = 0x40008000; - APBDEV_PMC_SECURE_SCRATCH100_0 = 0x40000; - APBDEV_PMC_SECURE_SCRATCH101_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH102_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH103_0 = 0x0; - APBDEV_PMC_SECURE_SCRATCH39_0 = (APBDEV_PMC_SECURE_SCRATCH39_0 & 0xF8000000) | 0x88; - - /* TODO: Do we want to bother locking the secure scratch registers? */ - /* 4.x Jamais Vu mitigations. */ - /* Overwrite exception vectors. */ - BPMP_VECTOR_RESET = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_UNDEF = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_SWI = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_PREFETCH_ABORT = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_DATA_ABORT = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_UNK = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_IRQ = BPMP_MITIGATION_RESET_VAL; - BPMP_VECTOR_FIQ = BPMP_MITIGATION_RESET_VAL; - - /* Disable AHB arbitration for the BPMP. */ - AHB_ARBITRATION_DISABLE_0 |= 2; - - /* Set SMMU for BPMP/APB-DMA to point to TZRAM. */ - MAKE_MC_REG(MC_SMMU_PTB_ASID) = 1; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_PTB_DATA) = 0x70012; - MAKE_MC_REG(MC_SMMU_AVPC_ASID) = 0x80000001; - MAKE_MC_REG(MC_SMMU_PPCS1_ASID) = 0x80000001; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_PTC_FLUSH) = 0; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - MAKE_MC_REG(MC_SMMU_TLB_FLUSH) = 0; - (void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG); - - /* Wait for the BPMP to halt. */ - while ((FLOW_CTLR_HALT_COP_EVENTS_0 >> 29) != 2) { - wait(1); - } - - /* If not in a debugging context, setup the activity monitor. */ - if ((get_debug_authentication_status() & 3) != 3) { - FLOW_CTLR_HALT_COP_EVENTS_0 = 0x40000000; - clkrst_reboot(CARDEVICE_ACTMON); - /* Sample every microsecond. */ - ACTMON_GLB_PERIOD_CTRL_0 = 0x100; - /* Fire interrupt every wakeup. */ - ACTMON_COP_UPPER_WMARK_0 = 0; - /* Cause a panic() on BPMP wakeup. */ - actmon_set_callback(actmon_on_bpmp_wakeup); - /* Enable interrupt when above watermark, periodic sampling. */ - ACTMON_COP_CTRL_0 = 0xC0040000; - } -} - -void setup_current_core_state(void) { - uint64_t temp_reg; - - /* Setup system registers. */ - SET_SYSREG(spsr_el3, 0b1111 << 6 | 0b0101); /* use EL2h+DAIF set initially, may be overwritten later. Not in official code */ - - SET_SYSREG(actlr_el3, 0x73ull); - SET_SYSREG(actlr_el2, 0x73ull); - SET_SYSREG(hcr_el2, 0x80000000ull); - SET_SYSREG(dacr32_el2, 0xFFFFFFFFull); - SET_SYSREG(sctlr_el1, 0xC50838ull); - SET_SYSREG(sctlr_el2, 0x30C50838ull); - - __isb(); - - SET_SYSREG(cntfrq_el0, SYSCTR0_CNTFID0_0); - SET_SYSREG(cnthctl_el2, 3ull); - - __isb(); - - /* Setup Interrupts, flow. */ - flow_clear_csr0_and_events(get_core_id()); - intr_initialize_gic(); - intr_set_priority(INTERRUPT_ID_1C, 0); - intr_set_group(INTERRUPT_ID_1C, 0); - intr_set_enabled(INTERRUPT_ID_1C, 1); - - /* Restore current core context. */ - restore_current_core_context(); -} - -void identity_unmap_iram_cd_tzram(void) { - /* See also: configure_ttbls (in coldboot_init.c). */ - /*uintptr_t *mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - uintptr_t *mmu_l2_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE);*/ - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - - mmu_unmap_range(3, mmu_l3_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_IRAM_A_CCRT0), IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_IRAM_A_CCRT0)); - mmu_unmap_range(3, mmu_l3_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_IRAM_CD), IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_IRAM_CD)); - /*mmu_unmap_range(3, mmu_l3_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_TZRAM), IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_TZRAM)); - - mmu_unmap(2, mmu_l2_tbl, 0x40000000); - mmu_unmap(2, mmu_l2_tbl, 0x7C000000); - - mmu_unmap(1, mmu_l1_tbl, 0x40000000);*/ - - tlb_invalidate_all_inner_shareable(); -} - -void secure_additional_devices(void) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 |= APB_SSER0_PMC; /* make PMC secure-only (2.x+) */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 |= APB_SSER1_MC0 | APB_SSER1_MC1 | APB_SSER1_MCB; /* make MC0, MC1, MCB secure-only (4.x+) */ - } - } -} - -void set_extabt_serror_taken_to_el3(bool taken_to_el3) { - uint64_t temp_scr_el3; - __asm__ __volatile__ ("mrs %0, scr_el3" : "=r"(temp_scr_el3) :: "memory"); - - temp_scr_el3 &= 0xFFFFFFF7; - temp_scr_el3 |= taken_to_el3 ? 8 : 0; - - __asm__ __volatile__ ("msr scr_el3, %0" :: "r"(temp_scr_el3) : "memory"); - __isb(); -} diff --git a/exosphere/src/bootup.h b/exosphere/src/bootup.h deleted file mode 100644 index ba8a20d20..000000000 --- a/exosphere/src/bootup.h +++ /dev/null @@ -1,115 +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_BOOTUP_H -#define EXOSPHERE_BOOTUP_H - -#include <stdint.h> - -/* 21.1.7 AP Control Registers */ -/* 21.1.7.1 APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 slaves */ -typedef enum { - APB_SSER0_MISC_REGS = 1 << 1, /* PP, SC1x pads and GP registers */ - APB_SSER0_SATA_AUX = 1 << 2, - APB_SSER0_PINMUX_AUX = 1 << 3, - APB_SSER0_APE = 1 << 4, - - APB_SSER0_DTV = 1 << 6, - - APB_SSER0_PWM = 1 << 8, /* PWFM */ - APB_SSER0_QSPI = 1 << 9, - APB_SSER0_CSITE = 1 << 10, /* Core Site */ - APB_SSER0_RTC = 1 << 11, - - APB_SSER0_PMC = 1 << 13, - APB_SSER0_SE = 1 << 14, /* Security Engine */ - APB_SSER0_FUSE = 1 << 15, - APB_SSER0_KFUSE = 1 << 16, - - APB_SSER0_UNUSED = 1 << 18, /* reserved, unused but listed as accessible */ - - APB_SSER0_SATA = 1 << 20, - APB_SSER0_HDA = 1 << 21, - APB_SSER0_LA = 1 << 22, - APB_SSER0_ATOMICS = 1 << 23, - APB_SSER0_CEC = 1 << 24, - - STM = 1 << 29 -} APB_SSER0; - -/* 21.1.7.2 APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 slaves */ -typedef enum { - APB_SSER1_MC0 = 1 << 4, - APB_SSER1_EMC0 = 1 << 5, - - APB_SSER1_MC1 = 1 << 8, - APB_SSER1_EMC1 = 1 << 9, - APB_SSER1_MCB = 1 << 10, - APB_SSER1_EMBC = 1 << 11, - APB_SSER1_UART_A = 1 << 12, - APB_SSER1_UART_B = 1 << 13, - APB_SSER1_UART_C = 1 << 14, - APB_SSER1_UART_D = 1 << 15, - - APB_SSER1_SPI1 = 1 << 20, - APB_SSER1_SPI2 = 1 << 21, - APB_SSER1_SPI3 = 1 << 22, - APB_SSER1_SPI4 = 1 << 23, - APB_SSER1_SPI5 = 1 << 24, - APB_SSER1_SPI6 = 1 << 25, - APB_SSER1_I2C1 = 1 << 26, - APB_SSER1_I2C2 = 1 << 27, - APB_SSER1_I2C3 = 1 << 28, - APB_SSER1_I2C4 = 1 << 29, - APB_SSER1_DVC = 1 << 30, - APB_SSER1_I2C5 = 1 << 30, - APB_SSER1_I2C6 = 1 << 31 /* this will show as negative because of the 32bit sign bit being set */ -} APB_SSER1; - -/* 21.1.7.3 APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 slaves */ -typedef enum { - APB_SSER2_SDMMC1 = 1 << 0, - APB_SSER2_SDMMC2 = 1 << 1, - APB_SSER2_SDMMC3 = 1 << 2, - APB_SSER2_SDMMC4 = 1 << 3, - - APB_SSER2_MIPIBIF = 1 << 7, /* reserved */ - APB_SSER2_DDS = 1 << 8, - APB_SSER2_DP2 = 1 << 9, - APB_SSER2_SOC_THERM = 1 << 10, - APB_SSER2_APB2JTAG = 1 << 11, - APB_SSER2_XUSB_HOST = 1 << 12, - APB_SSER2_XUSB_DEV = 1 << 13, - APB_SSER2_XUSB_PADCTL = 1 << 14, - APB_SSER2_MIPI_CAL = 1 << 15, - APB_SSER2_DVFS = 1 << 16 -} APB_SSER2; - -void bootup_misc_mmio(void); - -void setup_4x_mmio(void); - -void setup_dram_magic_numbers(void); - -void setup_current_core_state(void); - -void identity_unmap_iram_cd_tzram(void); - -void secure_additional_devices(void); - -void set_extabt_serror_taken_to_el3(bool taken_to_el3); - -#endif diff --git a/exosphere/src/bpmp.h b/exosphere/src/bpmp.h deleted file mode 100644 index 5222c8a18..000000000 --- a/exosphere/src/bpmp.h +++ /dev/null @@ -1,44 +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_BPMP_H -#define EXOSPHERE_BPMP_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere register definitions for the Tegra X1 BPMP vectors. */ - -static inline uintptr_t get_bpmp_vector_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_EXCEPTION_VECTORS); -} - -#define BPMP_VECTOR_BASE (get_bpmp_vector_base()) - -#define EVP_CPU_RESET_VECTOR_0 MAKE_REG32(BPMP_VECTOR_BASE + 0x100) - -#define BPMP_VECTOR_RESET MAKE_REG32(BPMP_VECTOR_BASE + 0x200) -#define BPMP_VECTOR_UNDEF MAKE_REG32(BPMP_VECTOR_BASE + 0x204) -#define BPMP_VECTOR_SWI MAKE_REG32(BPMP_VECTOR_BASE + 0x208) -#define BPMP_VECTOR_PREFETCH_ABORT MAKE_REG32(BPMP_VECTOR_BASE + 0x20C) -#define BPMP_VECTOR_DATA_ABORT MAKE_REG32(BPMP_VECTOR_BASE + 0x210) -#define BPMP_VECTOR_UNK MAKE_REG32(BPMP_VECTOR_BASE + 0x214) -#define BPMP_VECTOR_IRQ MAKE_REG32(BPMP_VECTOR_BASE + 0x218) -#define BPMP_VECTOR_FIQ MAKE_REG32(BPMP_VECTOR_BASE + 0x21C) - -#define BPMP_MITIGATION_RESET_VAL 0x7D000000 - -#endif diff --git a/exosphere/src/car.c b/exosphere/src/car.c deleted file mode 100644 index bc4bb47b0..000000000 --- a/exosphere/src/car.c +++ /dev/null @@ -1,87 +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 "timers.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: generic_panic(); - } -} - -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: generic_panic(); - } -} - -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}; - -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)); -} \ No newline at end of file diff --git a/exosphere/src/car.h b/exosphere/src/car.h deleted file mode 100644 index dad0e61ed..000000000 --- a/exosphere/src/car.h +++ /dev/null @@ -1,58 +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_CLOCK_AND_RESET_H -#define EXOSPHERE_CLOCK_AND_RESET_H - -#include <stdint.h> - -#include "memory_map.h" - -/* Exosphere Driver for the Tegra X1 Clock and Reset registers. */ - -#define CAR_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_CLKRST)) - -#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_RST_DEVICES_H_0 MAKE_CAR_REG(0x008) -#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 NUM_CAR_BANKS 7 - -typedef enum { - CARDEVICE_UARTA = 6, - CARDEVICE_UARTB = 7, - CARDEVICE_I2C1 = 12, - CARDEVICE_I2C5 = 47, - CARDEVICE_ACTMON = 119, - CARDEVICE_BPMP = 1 -} CarDevice; - -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 diff --git a/exosphere/src/coldboot_init.c b/exosphere/src/coldboot_init.c deleted file mode 100644 index 19f0576fb..000000000 --- a/exosphere/src/coldboot_init.c +++ /dev/null @@ -1,213 +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 <string.h> -#include "utils.h" -#include "arm.h" -#include "mmu.h" -#include "memory_map.h" -#include "arm.h" -#include "package2.h" -#include "timers.h" -#include "exocfg.h" - -#undef MAILBOX_NX_BOOTLOADER_BASE -#undef TIMERS_BASE -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (MMIO_GET_DEVICE_7X_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) : (MMIO_GET_DEVICE_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) -#define TIMERS_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_TMRs_WDTs)) - -extern const uint8_t __start_cold[]; - -/* warboot_init.c */ -extern unsigned int g_exosphere_target_firmware_for_init; -void init_dma_controllers(unsigned int target_firmware); -void set_memory_registers_enable_mmu_1x_ttbr0(void); -void set_memory_registers_enable_mmu_5x_ttbr0(void); - -static void identity_map_all_mappings(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl) { - static const uintptr_t addrs[] = { TUPLE_FOLD_LEFT_0(EVAL(IDENTIY_MAPPING_ID_MAX), _MMAPID, COMMA) }; - static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(IDENTIY_MAPPING_ID_MAX), _MMAPID, COMMA) }; - static const uint64_t attribs[] = { TUPLE_FOLD_LEFT_2(EVAL(IDENTIY_MAPPING_ID_MAX), _MMAPID, COMMA) }; - static const uint64_t is_block[] = { TUPLE_FOLD_LEFT_3(EVAL(IDENTIY_MAPPING_ID_MAX), _MMAPID, COMMA) }; - - for(size_t i = 0; i < IDENTIY_MAPPING_ID_MAX; i++) { - identity_map_mapping(mmu_l1_tbl, mmu_l3_tbl, addrs[i], sizes[i], attribs[i], is_block[i]); - } -} - -static void mmio_map_all_devices(uintptr_t *mmu_l3_tbl, unsigned int target_firmware) { - static const uintptr_t pas[] = { TUPLE_FOLD_LEFT_0(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; - static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; - static const bool is_secure[] = { TUPLE_FOLD_LEFT_2(EVAL(MMIO_DEVID_MAX), _MMAPDEV, COMMA) }; - - static const uintptr_t pas_7x[] = { TUPLE_FOLD_LEFT_0(EVAL(MMIO_DEVID_MAX), _MMAPDEV7X, COMMA) }; - - for(size_t i = 0, offset = 0; i < MMIO_DEVID_MAX; i++) { - uintptr_t pa = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? pas[i] : pas_7x[i]; - mmio_map_device(mmu_l3_tbl, MMIO_BASE + offset, pa, sizes[i], is_secure[i]); - offset += sizes[i]; - offset += 0x1000; - } -} - -static void lp0_entry_map_all_ram_segments(uintptr_t *mmu_l3_tbl) { - static const uintptr_t pas[] = { TUPLE_FOLD_LEFT_0(EVAL(LP0_ENTRY_RAM_SEGMENT_ID_MAX), _MMAPLP0ES, COMMA) }; - static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(LP0_ENTRY_RAM_SEGMENT_ID_MAX), _MMAPLP0ES, COMMA) }; - static const uint64_t attribs[] = { TUPLE_FOLD_LEFT_2(EVAL(LP0_ENTRY_RAM_SEGMENT_ID_MAX), _MMAPLP0ES, COMMA) }; - - for(size_t i = 0, offset = 0; i < LP0_ENTRY_RAM_SEGMENT_ID_MAX; i++) { - lp0_entry_map_ram_segment(mmu_l3_tbl, LP0_ENTRY_RAM_SEGMENT_BASE + offset, pas[i], sizes[i], attribs[i]); - offset += 0x10000; - } -} - -static void warmboot_map_all_ram_segments(uintptr_t *mmu_l3_tbl) { - static const uintptr_t pas[] = { TUPLE_FOLD_LEFT_0(EVAL(WARMBOOT_RAM_SEGMENT_ID_MAX), _MMAPWBS, COMMA) }; - static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(WARMBOOT_RAM_SEGMENT_ID_MAX), _MMAPWBS, COMMA) }; - static const uint64_t attribs[] = { TUPLE_FOLD_LEFT_2(EVAL(WARMBOOT_RAM_SEGMENT_ID_MAX), _MMAPWBS, COMMA) }; - - for(size_t i = 0, offset = 0; i < WARMBOOT_RAM_SEGMENT_ID_MAX; i++) { - warmboot_map_ram_segment(mmu_l3_tbl, WARMBOOT_RAM_SEGMENT_BASE + offset, pas[i], sizes[i], attribs[i]); - offset += sizes[i]; - } -} - -static void tzram_map_all_segments(uintptr_t *mmu_l3_tbl, unsigned int target_firmware) { - static const uintptr_t offs[] = { TUPLE_FOLD_LEFT_0(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZS, COMMA) }; - static const size_t sizes[] = { TUPLE_FOLD_LEFT_1(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZS, COMMA) }; - static const size_t increments[] = { TUPLE_FOLD_LEFT_2(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZS, COMMA) }; - static const bool is_executable[] = { TUPLE_FOLD_LEFT_3(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZS, COMMA) }; - - static const uintptr_t offs_5x[] = { TUPLE_FOLD_LEFT_0(EVAL(TZRAM_SEGMENT_ID_MAX), _MMAPTZ5XS, COMMA) }; - - for(size_t i = 0, offset = 0; i < TZRAM_SEGMENT_ID_MAX; i++) { - uintptr_t off = (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) ? offs[i] : offs_5x[i]; - tzram_map_segment(mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, 0x7C010000ull + off, sizes[i], is_executable[i]); - offset += increments[i]; - } -} - -static void configure_ttbls(unsigned int target_firmware) { - uintptr_t *mmu_l1_tbl; - uintptr_t *mmu_l2_tbl; - uintptr_t *mmu_l3_tbl; - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - mmu_l2_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); - mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - } else { - mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - mmu_l2_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); - mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - } - - mmu_init_table(mmu_l1_tbl, 64); /* 33-bit address space */ - mmu_init_table(mmu_l2_tbl, 4096); - /* - Nintendo uses the same L3 table for everything, but they make sure - nothing clashes. - */ - mmu_init_table(mmu_l3_tbl, 4096); - - mmu_map_table(1, mmu_l1_tbl, 0x40000000, mmu_l2_tbl, 0); - mmu_map_table(1, mmu_l1_tbl, 0x1C0000000, mmu_l2_tbl, 0); - - mmu_map_table(2, mmu_l2_tbl, 0x40000000, mmu_l3_tbl, 0); - mmu_map_table(2, mmu_l2_tbl, 0x7C000000, mmu_l3_tbl, 0); - mmu_map_table(2, mmu_l2_tbl, 0x1F0000000ull, mmu_l3_tbl, 0); - - identity_map_all_mappings(mmu_l1_tbl, mmu_l3_tbl); - mmio_map_all_devices(mmu_l3_tbl, target_firmware); - lp0_entry_map_all_ram_segments(mmu_l3_tbl); - warmboot_map_all_ram_segments(mmu_l3_tbl); - tzram_map_all_segments(mmu_l3_tbl, target_firmware); -} - -static void do_relocation(const coldboot_crt0_reloc_list_t *reloc_list, size_t index) { - extern const uint8_t __glob_origin__[]; - uint64_t *p_vma = (uint64_t *)reloc_list->relocs[index].vma; - bool is_clear = reloc_list->relocs[index].lma == 0; - size_t offset = reloc_list->relocs[index].lma - (uintptr_t)__glob_origin__; - const uint64_t *p_lma = (const uint64_t *)(reloc_list->reloc_base + offset); - size_t size = reloc_list->relocs[index].end_vma - reloc_list->relocs[index].vma; - - for(size_t i = 0; i < size / 8; i++) { - p_vma[i] = is_clear ? 0 : p_lma[i]; - } -} - -uintptr_t get_coldboot_crt0_temp_stack_address(void) { - return TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; -} - -uintptr_t get_coldboot_crt0_stack_address(void) { - if (exosphere_get_target_firmware_for_init() < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - return TZRAM_GET_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; - } else { - return TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; - } - -} - -void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, uintptr_t start_cold) { - //MAILBOX_NX_SECMON_BOOT_TIME = TIMERUS_CNTR_1US_0; - - /* Custom approach */ - reloc_list->reloc_base = start_cold; - - /* TODO: Set NX BOOTLOADER clock time field */ - - /* This at least copies .warm_crt0 to its VMA. */ - for(size_t i = 0; i < reloc_list->nb_relocs_pre_mmu_init; i++) { - do_relocation(reloc_list, i); - } - /* At this point, we can (and will) access functions located in .warm_crt0 */ - - /* - From https://events.static.linuxfound.org/sites/events/files/slides/slides_17.pdf : - Caches may write back dirty lines at any time: - - To make space for new allocations - - Even if MMU is off - - Even if Cacheable accesses are disabled (caches are never 'off') - - It should be fine to clear that here and not before. - */ - flush_dcache_all(); - invalidate_icache_all(); - - /* Set target firmware. */ - g_exosphere_target_firmware_for_init = exosphere_get_target_firmware_for_init(); - - /* Initialize DMA controllers, and write to AHB_GIZMO_TZRAM. */ - /* TZRAM accesses should work normally after this point. */ - init_dma_controllers(g_exosphere_target_firmware_for_init); - - configure_ttbls(g_exosphere_target_firmware_for_init); - if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - set_memory_registers_enable_mmu_1x_ttbr0(); - } else { - set_memory_registers_enable_mmu_5x_ttbr0(); - } - - /* Copy or clear the remaining sections */ - for(size_t i = 0; i < reloc_list->nb_relocs_post_mmu_init; i++) { - do_relocation(reloc_list, reloc_list->nb_relocs_pre_mmu_init + i); - } - - flush_dcache_all(); - invalidate_icache_all(); - /* At this point we can access all the mapped segments (all other functions, data...) normally */ -} diff --git a/exosphere/src/coldboot_main.c b/exosphere/src/coldboot_main.c deleted file mode 100644 index 0b0fb76c5..000000000 --- a/exosphere/src/coldboot_main.c +++ /dev/null @@ -1,36 +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 <string.h> -#include "utils.h" -#include "mmu.h" -#include "memory_map.h" -#include "arm.h" -#include "cpu_context.h" - -extern uint8_t __pk2ldr_start__[], __pk2ldr_end__[]; - -void coldboot_main(void) { - uintptr_t *mmu_l3_table = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - uintptr_t pk2ldr = TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_PK2LDR); - - /* Clear and unmap pk2ldr (which is reused as exception entry stacks) */ - memset((void *)pk2ldr, 0, __pk2ldr_end__ - __pk2ldr_start__); - mmu_unmap_range(3, mmu_l3_table, pk2ldr, __pk2ldr_end__ - __pk2ldr_start__); - tlb_invalidate_all_inner_shareable(); - - core_jump_to_lower_el(); -} diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c deleted file mode 100644 index a69de35b0..000000000 --- a/exosphere/src/configitem.c +++ /dev/null @@ -1,308 +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 <vapours/ams_version.h> - -#include "bootconfig.h" -#include "configitem.h" -#include "interrupt.h" -#include "package2.h" -#include "se.h" -#include "fuse.h" -#include "utils.h" -#include "masterkey.h" -#include "exocfg.h" -#include "smc_ams.h" -#include "arm.h" - -#define u8 uint8_t -#define u32 uint32_t -#include "rebootstub_bin.h" -#undef u8 -#undef u32 - -static bool g_hiz_mode_enabled = false; -static bool g_debugmode_override_user = false, g_debugmode_override_priv = false; -static bool g_enable_usermode_exception_handlers = true; -static bool g_enable_usermode_pmu_access = false; - -uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) { - switch (item) { - case CONFIGITEM_HIZMODE: - g_hiz_mode_enabled = (value != 0); - break; - case CONFIGITEM_NEEDS_REBOOT: - /* Force a reboot, if requested. */ - { - switch (value) { - case REBOOT_KIND_NO_REBOOT: - return 0; - case REBOOT_KIND_TO_RCM: - /* Set reboot kind = rcm. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2; - break; - case REBOOT_KIND_TO_WB_PAYLOAD: - /* Set reboot kind = warmboot. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x1; - /* Patch SDRAM init to perform an SVC immediately after second write */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x634ull) = 0x2E38DFFF; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x638ull) = 0x6001DC28; - /* Set SVC handler to jump to reboot stub in IRAM. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208; - - /* Copy reboot stub payload. */ - ams_map_irampage(0x4003F000); - for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) { - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + i) = read32le(rebootstub_bin, i); - } - ams_unmap_irampage(); - - /* Ensure stub is flushed. */ - flush_dcache_all(); - break; - default: - return 2; - } - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; - while (1) { } - } - break; - case CONFIGITEM_NEEDS_SHUTDOWN: - /* Force a shutdown, if requested. */ - { - if (value == 0) { - return 0; - } - /* Set reboot kind = warmboot. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x1; - /* Patch SDRAM init to perform an SVC immediately after second write */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x634ull) = 0x2E38DFFF; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x638ull) = 0x6001DC28; - /* Set SVC handler to jump to reboot stub in IRAM. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208; - - /* Copy reboot stub payload. */ - ams_map_irampage(0x4003F000); - for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) { - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + i) = read32le(rebootstub_bin, i); - } - /* Tell rebootstub to shut down. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + 0x10) = 0x0; - ams_unmap_irampage(); - - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; - while (1) { } - } - break; - default: - return 2; - } - - return 0; -} - -bool configitem_is_recovery_boot(void) { - uint64_t is_recovery_boot; - if (configitem_get(true, CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) { - generic_panic(); - } - - return is_recovery_boot != 0; -} - -bool configitem_is_retail(void) { - uint64_t is_retail; - if (configitem_get(true, CONFIGITEM_ISRETAIL, &is_retail) != 0) { - generic_panic(); - } - - return is_retail != 0; -} - -bool configitem_is_hiz_mode_enabled(void) { - return g_hiz_mode_enabled; -} - -void configitem_set_hiz_mode_enabled(bool enabled) { - g_hiz_mode_enabled = enabled; -} - -bool configitem_is_debugmode_priv(void) { - uint64_t debugmode = 0; - if (configitem_get(true, CONFIGITEM_ISDEBUGMODE, &debugmode) != 0) { - generic_panic(); - } - - return debugmode != 0; -} - -uint64_t configitem_get_hardware_type(void) { - uint64_t hardware_type; - if (configitem_get(true, CONFIGITEM_HARDWARETYPE, &hardware_type) != 0) { - generic_panic(); - } - return hardware_type; -} - -void configitem_set_debugmode_override(bool user, bool priv) { - g_debugmode_override_user = user; - g_debugmode_override_priv = priv; -} - -void configitem_disable_usermode_exception_handlers(void) { - g_enable_usermode_exception_handlers = false; -} - -void configitem_enable_usermode_pmu_access(void) { - g_enable_usermode_pmu_access = true; -} - -uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) { - uint32_t result = 0; - switch (item) { - case CONFIGITEM_DISABLEPROGRAMVERIFICATION: - *p_outvalue = (int)(bootconfig_disable_program_verification()); - break; - case CONFIGITEM_DRAMID: - *p_outvalue = fuse_get_dram_id(); - break; - case CONFIGITEM_SECURITYENGINEIRQ: - /* SE is interrupt #0x2C. */ - *p_outvalue = INTERRUPT_ID_USER_SECURITY_ENGINE; - break; - case CONFIGITEM_VERSION: - *p_outvalue = fuse_get_expected_fuse_version(exosphere_get_target_firmware()); - break; - case CONFIGITEM_HARDWARETYPE: - *p_outvalue = fuse_get_hardware_type(exosphere_get_target_firmware()); - break; - case CONFIGITEM_ISRETAIL: - *p_outvalue = fuse_get_retail_type(); - break; - case CONFIGITEM_ISRECOVERYBOOT: - *p_outvalue = (int)(bootconfig_is_recovery_boot()); - break; - case CONFIGITEM_DEVICEID: - *p_outvalue = fuse_get_device_id(); - break; - case CONFIGITEM_BOOTREASON: - /* For some reason, Nintendo removed it on 4.0 */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - *p_outvalue = bootconfig_get_boot_reason(); - } else { - result = 2; - } - break; - case CONFIGITEM_MEMORYARRANGE: - *p_outvalue = bootconfig_get_memory_arrangement(); - break; - case CONFIGITEM_ISDEBUGMODE: - if ((privileged && g_debugmode_override_priv) || (!privileged && g_debugmode_override_user)) { - *p_outvalue = 1; - } else { - *p_outvalue = (int)(bootconfig_is_debug_mode()); - } - break; - case CONFIGITEM_KERNELCONFIGURATION: - { - uint64_t config = bootconfig_get_kernel_configuration(); - /* Enable usermode exception handlers by default. */ - if (g_enable_usermode_exception_handlers) { - config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS; - } - /* Allow for enabling usermode pmu access. */ - if (g_enable_usermode_pmu_access) { - config |= KERNELCONFIGFLAG_ENABLE_USER_PMU_ACCESS; - } - *p_outvalue = config; - } - break; - case CONFIGITEM_HIZMODE: - *p_outvalue = (int)g_hiz_mode_enabled; - break; - case CONFIGITEM_ISQUESTUNIT: - /* Added on 3.0, used to determine whether console is a kiosk unit. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - *p_outvalue = (fuse_get_reserved_odm(4) >> 10) & 1; - } else { - result = 2; - } - break; - case CONFIGITEM_NEWHARDWARETYPE_5X: - /* Added in 5.x, currently hardcoded to 0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - *p_outvalue = 0; - } else { - result = 2; - } - break; - case CONFIGITEM_NEWKEYGENERATION_5X: - /* Added in 5.x. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - *p_outvalue = fuse_get_5x_key_generation(); - } else { - result = 2; - } - break; - case CONFIGITEM_PACKAGE2HASH_5X: - /* Added in 5.x. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 && bootconfig_is_recovery_boot()) { - bootconfig_get_package2_hash_for_recovery(p_outvalue); - } else { - result = 2; - } - break; - case CONFIGITEM_EXOSPHERE_VERSION: - /* UNOFFICIAL: Gets information about the current exosphere version. */ - *p_outvalue = ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56ull) | - ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48ull) | - ((uint64_t)(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40ull) | - ((uint64_t)(mkey_get_revision() & 0xFF) << 32ull) | - ((uint64_t)(exosphere_get_target_firmware()) << 0ull); - break; - case CONFIGITEM_NEEDS_REBOOT: - /* UNOFFICIAL: The fact that we are executing means we aren't in the process of rebooting. */ - *p_outvalue = 0; - break; - case CONFIGITEM_NEEDS_SHUTDOWN: - /* UNOFFICIAL: The fact that we are executing means we aren't in the process of shutting down. */ - *p_outvalue = 0; - break; - case CONFIGITEM_EXOSPHERE_VERHASH: - /* UNOFFICIAL: Gets information about the current exosphere git commit hash. */ - *p_outvalue = ATMOSPHERE_RELEASE_VERSION_HASH; - break; - case CONFIGITEM_HAS_RCM_BUG_PATCH: - /* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */ - *p_outvalue = (int)(fuse_has_rcm_bug_patch()); - break; - case CONFIGITEM_SHOULD_BLANK_PRODINFO: - /* UNOFFICIAL: Gets whether this unit should simulate a "blanked" PRODINFO. */ - *p_outvalue = exosphere_should_blank_prodinfo(); - break; - case CONFIGITEM_ALLOW_CAL_WRITES: - /* UNOFFICIAL: Gets whether this unit should allow writing to the calibration partition. */ - *p_outvalue = exosphere_should_allow_writing_to_cal(); - break; - default: - result = 2; - break; - } - return result; -} diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h deleted file mode 100644 index 103b07c15..000000000 --- a/exosphere/src/configitem.h +++ /dev/null @@ -1,71 +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_CFG_ITEM_H -#define EXOSPHERE_CFG_ITEM_H - -#include <stdbool.h> -#include <stdint.h> - -typedef enum { - CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1, - CONFIGITEM_DRAMID = 2, - CONFIGITEM_SECURITYENGINEIRQ = 3, - CONFIGITEM_VERSION = 4, - CONFIGITEM_HARDWARETYPE = 5, - CONFIGITEM_ISRETAIL = 6, - CONFIGITEM_ISRECOVERYBOOT = 7, - CONFIGITEM_DEVICEID = 8, - CONFIGITEM_BOOTREASON = 9, - CONFIGITEM_MEMORYARRANGE = 10, - CONFIGITEM_ISDEBUGMODE = 11, - CONFIGITEM_KERNELCONFIGURATION = 12, - CONFIGITEM_HIZMODE = 13, - CONFIGITEM_ISQUESTUNIT = 14, - CONFIGITEM_NEWHARDWARETYPE_5X = 15, - CONFIGITEM_NEWKEYGENERATION_5X = 16, - CONFIGITEM_PACKAGE2HASH_5X = 17, - - /* These are unofficial, for usage by Exosphere. */ - 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, -} ConfigItem; - -#define REBOOT_KIND_NO_REBOOT 0 -#define REBOOT_KIND_TO_RCM 1 -#define REBOOT_KIND_TO_WB_PAYLOAD 2 - -uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value); -uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue); - -bool configitem_is_recovery_boot(void); -bool configitem_is_retail(void); -bool configitem_is_hiz_mode_enabled(void); -bool configitem_is_debugmode_priv(void); - -void configitem_set_debugmode_override(bool user, bool priv); -void configitem_disable_usermode_exception_handlers(void); -void configitem_enable_usermode_pmu_access(void); -void configitem_set_hiz_mode_enabled(bool enabled); - -uint64_t configitem_get_hardware_type(void); - -#endif diff --git a/exosphere/src/cpu_context.c b/exosphere/src/cpu_context.c deleted file mode 100644 index 914467194..000000000 --- a/exosphere/src/cpu_context.c +++ /dev/null @@ -1,235 +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 <stdbool.h> -#include <stdint.h> -#include "arm.h" -#include "cpu_context.h" -#include "car.h" -#include "exocfg.h" -#include "flow.h" -#include "pmc.h" -#include "timers.h" -#include "smc_api.h" -#include "utils.h" -#include "synchronization.h" -#include "preprocessor.h" - -#define SAVE_SYSREG64(reg) do { __asm__ __volatile__ ("mrs %0, " #reg : "=r"(temp_reg) :: "memory"); g_cpu_contexts[current_core].reg = temp_reg; } while(false) -#define SAVE_SYSREG32(reg) do { __asm__ __volatile__ ("mrs %0, " #reg : "=r"(temp_reg) :: "memory"); g_cpu_contexts[current_core].reg = (uint32_t)temp_reg; } while(false) -#define SAVE_BP_REG(i, _) SAVE_SYSREG64(DBGBVR##i##_EL1); SAVE_SYSREG64(DBGBCR##i##_EL1); -#define SAVE_WP_REG(i, _) SAVE_SYSREG64(DBGWVR##i##_EL1); SAVE_SYSREG64(DBGWCR##i##_EL1); - -#define RESTORE_SYSREG64(reg) do { temp_reg = g_cpu_contexts[current_core].reg; __asm__ __volatile__ ("msr " #reg ", %0" :: "r"(temp_reg) : "memory"); } while(false) -#define RESTORE_SYSREG32(reg) RESTORE_SYSREG64(reg) -#define RESTORE_BP_REG(i, _) RESTORE_SYSREG64(DBGBVR##i##_EL1); RESTORE_SYSREG64(DBGBCR##i##_EL1); -#define RESTORE_WP_REG(i, _) RESTORE_SYSREG64(DBGWVR##i##_EL1); RESTORE_SYSREG64(DBGWCR##i##_EL1); - -/* start.s */ -void __attribute__((noreturn)) __jump_to_lower_el(uint64_t arg, uintptr_t ep, uint32_t spsr); - -static saved_cpu_context_t g_cpu_contexts[NUM_CPU_CORES] = {0}; - -void use_core_entrypoint_and_argument(uint32_t core, uintptr_t *entrypoint_addr, uint64_t *argument) { - saved_cpu_context_t *ctx = &g_cpu_contexts[core]; - if(ctx->ELR_EL3 == 0 || ctx->is_active) { - panic(0xF7F00007); /* invalid context */ - } - - *entrypoint_addr = ctx->ELR_EL3; - *argument = ctx->argument; - - ctx->ELR_EL3 = 0; - ctx->argument = 0; - ctx->is_active = 1; -} - -void set_core_entrypoint_and_argument(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { - g_cpu_contexts[core].ELR_EL3 = entrypoint_addr; - g_cpu_contexts[core].argument = argument; -} - -static __attribute__((target("cmodel=large"), noinline)) -critical_section_t *get_boot_critical_section(void) { - return &g_boot_critical_section; -} - -void __attribute__((noreturn)) core_jump_to_lower_el(void) { - uintptr_t ep; - uint64_t arg; - unsigned int core_id = get_core_id(); - uint32_t spsr = get_spsr(); - critical_section_t *critsec = get_boot_critical_section(); - - use_core_entrypoint_and_argument(core_id, &ep, &arg); - critical_section_leave(critsec); - flush_dcache_range(critsec, (uint8_t *)critsec + sizeof(critical_section_t)); - /* already does a dsb sy */ - __sev(); - - /* Nintendo hardcodes EL1, but we can boot fine using other EL1/EL2 modes as well */ - __jump_to_lower_el(arg, ep, 0b1111 << 6 | (spsr & 0b1101)); /* only keep EL, SPSel, set DAIF */ -} - -uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { - /* Is core valid? */ - if (core >= NUM_CPU_CORES) { - return 0xFFFFFFFE; - } - - /* Is core already on? */ - if (g_cpu_contexts[core].is_active) { - return 0xFFFFFFFC; - } - - set_core_entrypoint_and_argument(core, entrypoint_addr, argument); - - static const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; - static const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - /* Reset the core */ - CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 = (1 << (core + 0x10)) | (1 << core); - } - - /* Check if we're already in the correct state. */ - if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) != status_masks[core]) { - uint32_t counter = 5001; - - /* Poll the start bit until 0 */ - while (APBDEV_PMC_PWRGATE_TOGGLE_0 & 0x100) { - wait(1); - counter--; - if (counter < 1) { - goto CPU_ON_SUCCESS; - } - } - - /* Program PWRGATE_TOGGLE with the START bit set to 1, selecting CE[N] */ - APBDEV_PMC_PWRGATE_TOGGLE_0 = toggle_vals[core] | 0x100; - - /* Poll until we're in the correct state. */ - counter = 5001; - while (counter > 0) { - if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) == status_masks[core]) { - break; - } - wait(1); - counter--; - } - } - -CPU_ON_SUCCESS: - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - /* Start the core */ - CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 = (1 << (core + 0x10)) | (1 << core); - } - - return 0; -} - -void power_down_current_core(void) { - unsigned int current_core = get_core_id(); - flow_set_csr(current_core, 0); - flow_set_halt_events(current_core, false); - flow_set_cc4_ctrl(current_core, 0); - save_current_core_context(); - g_cpu_contexts[current_core].is_active = 0; - flush_dcache_all(); - finalize_powerdown(); -} - -uint32_t cpu_off(void) { - unsigned int current_core = get_core_id(); - if (current_core == 3) { - power_down_current_core(); - } else { - clear_priv_smc_in_progress(); - call_with_stack_pointer(get_exception_entry_stack_address(current_core), power_down_current_core); - } - while (true) { - /* Wait forever. */ - } - return 0; -} - -void save_current_core_context(void) { - unsigned int current_core = get_core_id(); - uint64_t temp_reg = 1; - /* Write 1 to OS lock .*/ - __asm__ __volatile__ ("msr oslar_el1, %0" : : "r"(temp_reg)); - - /* Save system registers. */ - - SAVE_SYSREG32(OSDTRRX_EL1); - SAVE_SYSREG32(OSDTRTX_EL1); - SAVE_SYSREG32(MDSCR_EL1); - SAVE_SYSREG32(OSECCR_EL1); - SAVE_SYSREG32(MDCCINT_EL1); - SAVE_SYSREG32(DBGCLAIMCLR_EL1); - SAVE_SYSREG32(DBGVCR32_EL2); - SAVE_SYSREG32(SDER32_EL3); - SAVE_SYSREG32(MDCR_EL2); - SAVE_SYSREG32(MDCR_EL3); - SAVE_SYSREG32(SPSR_EL3); - - EVAL(REPEAT(6, SAVE_BP_REG, ~)); - EVAL(REPEAT(4, SAVE_WP_REG, ~)); - - /* Mark context as saved. */ - g_cpu_contexts[current_core].is_saved = 1; -} - -void restore_current_core_context(void) { - unsigned int current_core = get_core_id(); - uint64_t temp_reg; - - if (g_cpu_contexts[current_core].is_saved) { - RESTORE_SYSREG32(OSDTRRX_EL1); - RESTORE_SYSREG32(OSDTRTX_EL1); - RESTORE_SYSREG32(MDSCR_EL1); - RESTORE_SYSREG32(OSECCR_EL1); - RESTORE_SYSREG32(MDCCINT_EL1); - RESTORE_SYSREG32(DBGCLAIMCLR_EL1); - RESTORE_SYSREG32(DBGVCR32_EL2); - RESTORE_SYSREG32(SDER32_EL3); - RESTORE_SYSREG32(MDCR_EL2); - RESTORE_SYSREG32(MDCR_EL3); - RESTORE_SYSREG32(SPSR_EL3); - - EVAL(REPEAT(6, RESTORE_BP_REG, ~)); - EVAL(REPEAT(4, RESTORE_WP_REG, ~)); - - g_cpu_contexts[current_core].is_saved = 0; - } -} - -bool is_core_active(uint32_t core) { - return g_cpu_contexts[core].is_active != 0; -} - -void set_core_is_active(uint32_t core, bool is_active) { - g_cpu_contexts[core].is_active = (is_active) ? 1 : 0; -} - -void set_current_core_active(void) { - set_core_is_active(get_core_id(), true); -} - -void set_current_core_inactive(void) { - set_core_is_active(get_core_id(), false); -} - diff --git a/exosphere/src/cpu_context.h b/exosphere/src/cpu_context.h deleted file mode 100644 index dbdec20c8..000000000 --- a/exosphere/src/cpu_context.h +++ /dev/null @@ -1,83 +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_CPU_CTX_H -#define EXOSPHERE_CPU_CTX_H - -#include "utils.h" -#include "synchronization.h" - -/* Exosphere CPU Management functionality. */ - -extern critical_section_t g_boot_critical_section; - -typedef struct { - uint64_t argument; - uint64_t ELR_EL3; - int is_active; - int is_saved; - uint32_t OSDTRRX_EL1; - uint32_t OSDTRTX_EL1; - uint32_t MDSCR_EL1; - uint32_t OSECCR_EL1; - uint32_t MDCCINT_EL1; - uint32_t DBGCLAIMCLR_EL1; - uint32_t DBGVCR32_EL2; - uint32_t SDER32_EL3; - uint32_t MDCR_EL2; - uint32_t MDCR_EL3; - uint32_t SPSR_EL3; /* not in official code */ - uint64_t DBGBVR0_EL1; - uint64_t DBGBCR0_EL1; - uint64_t DBGBVR1_EL1; - uint64_t DBGBCR1_EL1; - uint64_t DBGBVR2_EL1; - uint64_t DBGBCR2_EL1; - uint64_t DBGBVR3_EL1; - uint64_t DBGBCR3_EL1; - uint64_t DBGBVR4_EL1; - uint64_t DBGBCR4_EL1; - uint64_t DBGBVR5_EL1; - uint64_t DBGBCR5_EL1; - uint64_t DBGWVR0_EL1; - uint64_t DBGWCR0_EL1; - uint64_t DBGWVR1_EL1; - uint64_t DBGWCR1_EL1; - uint64_t DBGWVR2_EL1; - uint64_t DBGWCR2_EL1; - uint64_t DBGWVR3_EL1; - uint64_t DBGWCR3_EL1; -} saved_cpu_context_t; - -#define NUM_CPU_CORES 4 - -void save_current_core_context(void); -void restore_current_core_context(void); - -bool is_core_active(uint32_t core); -void set_core_is_active(uint32_t core, bool is_active); -void set_current_core_active(void); -void set_current_core_inactive(void); - -void use_core_entrypoint_and_argument(uint32_t core, uintptr_t *entrypoint_addr, uint64_t *argument); -void set_core_entrypoint_and_argument(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument); -void __attribute__((noreturn)) core_jump_to_lower_el(void); - -uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument); -uint32_t cpu_off(void); - - -#endif diff --git a/exosphere/src/emummc_cfg.h b/exosphere/src/emummc_cfg.h deleted file mode 100644 index b8d0f90be..000000000 --- a/exosphere/src/emummc_cfg.h +++ /dev/null @@ -1,67 +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_EMUMMC_CONFIG_H -#define EXOSPHERE_EMUMMC_CONFIG_H - -#include <stdint.h> -#include <vapours/ams_version.h> -#include "utils.h" - -/* "EFS0" */ -#define MAGIC_EMUMMC_CONFIG (0x30534645) - -#define EMUMMC_FILE_PATH_MAX (0x80) - -typedef enum { - EMUMMC_TYPE_NONE = 0, - EMUMMC_TYPE_PARTITION = 1, - EMUMMC_TYPE_FILES = 2, -} emummc_type_t; - -typedef enum { - EMUMMC_MMC_NAND = 0, - EMUMMC_MMC_SD = 1, - EMUMMC_MMC_GC = 2, -} emummc_mmc_t; - -typedef struct { - uint32_t magic; - uint32_t type; - uint32_t id; - uint32_t fs_version; -} emummc_base_config_t; - -typedef struct { - uint64_t start_sector; -} emummc_partition_config_t; - -typedef struct { - char path[EMUMMC_FILE_PATH_MAX]; -} emummc_file_config_t; - -typedef struct { - emummc_base_config_t base_cfg; - union { - emummc_partition_config_t partition_cfg; - emummc_file_config_t file_cfg; - }; - char emu_dir_path[EMUMMC_FILE_PATH_MAX]; -} exo_emummc_config_t; - -_Static_assert(sizeof(exo_emummc_config_t) == 0x110, "exo_emummc_config_t definition!"); - -#endif diff --git a/exosphere/src/exceptions.s b/exosphere/src/exceptions.s deleted file mode 100644 index 501efe0ae..000000000 --- a/exosphere/src/exceptions.s +++ /dev/null @@ -1,254 +0,0 @@ -/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */ -/* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - - -/* - * Declare the exception vector table, enforcing it is aligned on a - * 2KB boundary, as required by the ARMv8 architecture. - * Use zero bytes as the fill value to be stored in the padding bytes - * so that it inserts illegal AArch64 instructions. This increases - * security, robustness and potentially facilitates debugging. - */ -.macro vector_base label, section_name=.vectors -.section \section_name, "ax" -.align 11, 0 -\label: -.endm - -/* - * Create an entry in the exception vector table, enforcing it is - * aligned on a 128-byte boundary, as required by the ARMv8 architecture. - * Use zero bytes as the fill value to be stored in the padding bytes - * so that it inserts illegal AArch64 instructions. This increases - * security, robustness and potentially facilitates debugging. - */ -.macro vector_entry label, section_name=.vectors -.cfi_sections .debug_frame -.section \section_name, "ax" -.align 7, 0 -.type \label, %function -.func \label -.cfi_startproc -\label: -.endm - -/* - * This macro verifies that the given vector doesnt exceed the - * architectural limit of 32 instructions. This is meant to be placed - * immediately after the last instruction in the vector. It takes the - * vector entry as the parameter - */ -.macro check_vector_size since - .endfunc - .cfi_endproc - .if (. - \since) > (32 * 4) - .error "Vector exceeds 32 instructions" - .endif -.endm - -/* Actual Vectors for Exosphere. */ -.global exosphere_vectors -vector_base exosphere_vectors - -/* Current EL, SP0 */ -.global unknown_exception -unknown_exception: -vector_entry synch_sp0 - /* Panic with color FF7700, code 10. */ - mov x0, #0x10 - movk x0, #0x07F0,lsl#16 - b panic - check_vector_size synch_sp0 - -vector_entry irq_sp0 - b unknown_exception - check_vector_size irq_sp0 - -vector_entry fiq_sp0 - b unknown_exception - check_vector_size fiq_sp0 - -vector_entry serror_sp0 - b unknown_exception - check_vector_size serror_sp0 - -/* Current EL, SPx */ -vector_entry synch_spx - b unknown_exception - check_vector_size synch_spx - -vector_entry irq_spx - b unknown_exception - check_vector_size irq_spx - -vector_entry fiq_spx - b unknown_exception - check_vector_size fiq_spx - -vector_entry serror_spx - b unknown_exception - check_vector_size serror_spx - -/* Lower EL, A64 */ -vector_entry synch_a64 - stp x29, x30, [sp, #-0x10]! - /* Verify SMC. */ - mrs x30, esr_el3 - lsr w29, w30, #0x1A - cmp w29, #0x17 - ldp x29, x30, [sp],#0x10 - b.ne unknown_exception - /* Call appropriate handler. */ - stp x29, x30, [sp, #-0x10]! - mrs x29, mpidr_el1 - and x29, x29, #0x3 - cmp x29, #0x3 - b.ne handle_core012_smc_exception - bl handle_core3_smc_exception - ldp x29, x30, [sp],#0x10 - eret - check_vector_size synch_a64 - -vector_entry irq_a64 - b unknown_exception - check_vector_size irq_a64 - -vector_entry fiq_a64 - stp x29, x30, [sp, #-0x10]! - mrs x29, mpidr_el1 - and x29, x29, #0x3 - cmp x29, #0x3 - b.ne unknown_exception - stp x28, x29, [sp, #-0x10]! - stp x26, x27, [sp, #-0x10]! - bl handle_fiq_exception - ldp x26, x27, [sp],#0x10 - ldp x28, x29, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - eret - check_vector_size fiq_a64 - -vector_entry serror_a64 - b unknown_exception - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_core012_smc_exception -.type handle_core012_smc_exception, %function -handle_core012_smc_exception: - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - bl set_priv_smc_in_progress - bl get_smc_core012_stack_address - mov x29, x0 - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - mov x30, sp - mov sp, x29 - stp x29, x30, [sp, #-0x10]! - bl handle_core3_smc_exception - ldp x29, x30, [sp],#0x10 - mov sp, x30 - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - bl clear_priv_smc_in_progress - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - eret - -/* Lower EL, A32 */ -vector_entry synch_a32 - b unknown_exception - check_vector_size synch_a32 - -vector_entry irq_a32 - b unknown_exception - check_vector_size irq_a32 - -vector_entry fiq_a32 - b fiq_a64 - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_fiq_exception -.type handle_fiq_exception, %function -handle_fiq_exception: - stp x29, x30, [sp, #-0x10]! - stp x24, x25, [sp, #-0x10]! - stp x22, x23, [sp, #-0x10]! - stp x20, x21, [sp, #-0x10]! - stp x18, x19, [sp, #-0x10]! - stp x16, x17, [sp, #-0x10]! - stp x14, x15, [sp, #-0x10]! - stp x12, x13, [sp, #-0x10]! - stp x10, x11, [sp, #-0x10]! - stp x8, x9, [sp, #-0x10]! - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - bl handle_registered_interrupt - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - ldp x8, x9, [sp],#0x10 - ldp x10, x11, [sp],#0x10 - ldp x12, x13, [sp],#0x10 - ldp x14, x15, [sp],#0x10 - ldp x16, x17, [sp],#0x10 - ldp x18, x19, [sp],#0x10 - ldp x20, x21, [sp],#0x10 - ldp x22, x23, [sp],#0x10 - ldp x24, x25, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - ret - -vector_entry serror_a32 - b unknown_exception - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_core3_smc_exception -.type handle_core3_smc_exception, %function -handle_core3_smc_exception: - stp x29, x30, [sp, #-0x10]! - stp x18, x19, [sp, #-0x10]! - stp x16, x17, [sp, #-0x10]! - stp x14, x15, [sp, #-0x10]! - stp x12, x13, [sp, #-0x10]! - stp x10, x11, [sp, #-0x10]! - stp x8, x9, [sp, #-0x10]! - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - mrs x0, esr_el3 - and x0, x0, #0xFFFF - mov x1, sp - bl call_smc_handler - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - ldp x8, x9, [sp],#0x10 - ldp x10, x11, [sp],#0x10 - ldp x12, x13, [sp],#0x10 - ldp x14, x15, [sp],#0x10 - ldp x16, x17, [sp],#0x10 - ldp x18, x19, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - ret diff --git a/exosphere/src/exocfg.c b/exosphere/src/exocfg.c deleted file mode 100644 index f757f9824..000000000 --- a/exosphere/src/exocfg.c +++ /dev/null @@ -1,124 +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 <stdbool.h> - -#include "utils.h" -#include "exocfg.h" -#include "mmu.h" -#include "memory_map.h" - -static exosphere_config_t g_exosphere_cfg = {MAGIC_EXOSPHERE_CONFIG, ATMOSPHERE_TARGET_FIRMWARE_CURRENT, EXOSPHERE_FLAGS_DEFAULT}; -static bool g_has_loaded_config = false; - -#define EXOSPHERE_CHECK_FLAG(flag) ((g_exosphere_cfg.flags & flag) != 0) - -static unsigned int exosphere_is_emummc() { - return g_exosphere_cfg.emummc_cfg.base_cfg.magic == MAGIC_EMUMMC_CONFIG && g_exosphere_cfg.emummc_cfg.base_cfg.type != EMUMMC_TYPE_NONE; -} - -/* Read config out of IRAM, return target firmware version. */ -unsigned int exosphere_load_config(void) { - if (g_has_loaded_config) { - generic_panic(); - } - g_has_loaded_config = true; - - const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG.magic; - - if (magic == MAGIC_EXOSPHERE_CONFIG) { - g_exosphere_cfg = MAILBOX_EXOSPHERE_CONFIG; - } - - return g_exosphere_cfg.target_firmware; -} - -unsigned int exosphere_get_target_firmware(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return g_exosphere_cfg.target_firmware; -} - -unsigned int exosphere_should_perform_620_keygen(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return false; -} - -unsigned int exosphere_should_override_debugmode_priv(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV); -} - -unsigned int exosphere_should_override_debugmode_user(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_IS_DEBUGMODE_USER); -} - -unsigned int exosphere_should_disable_usermode_exception_handlers(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS); -} - -unsigned int exosphere_should_enable_usermode_pmu_access(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS); -} - -unsigned int exosphere_should_blank_prodinfo(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_BLANK_PRODINFO); -} - -unsigned int exosphere_should_allow_writing_to_cal(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - if (exosphere_is_emummc()) { - return 1; - } else { - return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC); - } -} - -const exo_emummc_config_t *exosphere_get_emummc_config(void) { - if (!g_has_loaded_config) { - generic_panic(); - } - - return &g_exosphere_cfg.emummc_cfg; -} diff --git a/exosphere/src/exocfg.h b/exosphere/src/exocfg.h deleted file mode 100644 index 6be2787b7..000000000 --- a/exosphere/src/exocfg.h +++ /dev/null @@ -1,79 +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_EXOSPHERE_CONFIG_H -#define EXOSPHERE_EXOSPHERE_CONFIG_H - -#include <stdint.h> -#include <vapours/ams_version.h> -#include "utils.h" - -#include "memory_map.h" -#include "emummc_cfg.h" - -/* This serves to set configuration for *exosphere itself*, separate from the SecMon Exosphere mimics. */ - -/* "EXO0" */ -#define MAGIC_EXOSPHERE_CONFIG (0x304F5845) - -#define EXOSPHERE_LOOSEN_PACKAGE2_RESTRICTIONS_FOR_DEBUG 1 - -#define MAILBOX_EXOSPHERE_CONFIG (*((volatile exosphere_config_t *)(0x8000F000ull))) - -/* Exosphere config in DRAM shares physical/virtual mapping. */ -#define MAILBOX_EXOSPHERE_CONFIG_PHYS MAILBOX_EXOSPHERE_CONFIG - -#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN_DEPRECATED (1 << 0u) -#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u) -#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u) -#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u) -#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u) -#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u) -#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u) -#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV) - -typedef struct { - uint32_t magic; - uint32_t target_firmware; - uint32_t flags; - uint32_t reserved[5]; - exo_emummc_config_t emummc_cfg; -} exosphere_config_t; - -_Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t), "exosphere config definition"); - -unsigned int exosphere_load_config(void); -unsigned int exosphere_get_target_firmware(void); -unsigned int exosphere_should_perform_620_keygen(void); -unsigned int exosphere_should_override_debugmode_priv(void); -unsigned int exosphere_should_override_debugmode_user(void); -unsigned int exosphere_should_disable_usermode_exception_handlers(void); -unsigned int exosphere_should_enable_usermode_pmu_access(void); -unsigned int exosphere_should_blank_prodinfo(void); -unsigned int exosphere_should_allow_writing_to_cal(void); - -const exo_emummc_config_t *exosphere_get_emummc_config(void); - -static inline unsigned int exosphere_get_target_firmware_for_init(void) { - const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic; - if (magic == MAGIC_EXOSPHERE_CONFIG) { - return MAILBOX_EXOSPHERE_CONFIG_PHYS.target_firmware; - } else { - return ATMOSPHERE_TARGET_FIRMWARE_CURRENT; - } -} - -#endif diff --git a/exosphere/src/flow.h b/exosphere/src/flow.h deleted file mode 100644 index e24696116..000000000 --- a/exosphere/src/flow.h +++ /dev/null @@ -1,69 +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_FLOW_CTLR_H -#define EXOSPHERE_FLOW_CTLR_H - -#include <stdint.h> -#include <stdbool.h> -#include "cpu_context.h" -#include "memory_map.h" - -/* Exosphere register definitions for the Tegra X1 Flow Controller. */ - -static inline uintptr_t get_flow_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_FLOWCTRL); -} - -#define FLOW_BASE (get_flow_base()) - -#define MAKE_FLOW_REG(ofs) MAKE_REG32(FLOW_BASE + ofs) - -#define FLOW_CTLR_HALT_COP_EVENTS_0 MAKE_FLOW_REG(0x004) -#define FLOW_CTLR_FLOW_DBG_QUAL_0 MAKE_FLOW_REG(0x050) -#define FLOW_CTLR_L2FLUSH_CONTROL_0 MAKE_FLOW_REG(0x094) -#define FLOW_CTLR_BPMP_CLUSTER_CONTROL_0 MAKE_FLOW_REG(0x098) - - -static const struct { - unsigned int CPUN_CSR_OFS; - unsigned int HALT_CPUN_EVENTS_OFS; - unsigned int CC4_COREN_CTRL_OFS; -} g_flow_core_offsets[NUM_CPU_CORES] = { - {0x008, 0x000, 0x06C}, - {0x018, 0x014, 0x070}, - {0x020, 0x01C, 0x074}, - {0x028, 0x024, 0x078}, -}; - -static inline void flow_set_cc4_ctrl(uint32_t core, uint32_t cc4_ctrl) { - MAKE_FLOW_REG(g_flow_core_offsets[core].CC4_COREN_CTRL_OFS) = cc4_ctrl; -} - -static inline void flow_set_halt_events(uint32_t core, bool halt_events) { - MAKE_FLOW_REG(g_flow_core_offsets[core].HALT_CPUN_EVENTS_OFS) = (halt_events ? 0x40000F00 : 0x40000000); -} - -static inline void flow_set_csr(uint32_t core, uint32_t csr) { - MAKE_FLOW_REG(g_flow_core_offsets[core].CPUN_CSR_OFS) = (0x100 << core) | (csr << 12) | 0xC001; -} - -static inline void flow_clear_csr0_and_events(uint32_t core) { - MAKE_FLOW_REG(g_flow_core_offsets[core].CPUN_CSR_OFS) = 0; - MAKE_FLOW_REG(g_flow_core_offsets[core].HALT_CPUN_EVENTS_OFS) = 0; -} - -#endif diff --git a/exosphere/src/fuse.c b/exosphere/src/fuse.c deleted file mode 100644 index b10b6f7a8..000000000 --- a/exosphere/src/fuse.c +++ /dev/null @@ -1,335 +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 <string.h> -#include <vapours/ams_version.h> - -#include "car.h" -#include "fuse.h" -#include "masterkey.h" -#include "pmc.h" -#include "timers.h" - -static bool g_has_checked_for_rcm_bug_patch = false; -static bool g_has_rcm_bug_patch = false; - -/* Prototypes for internal commands. */ -void fuse_enable_power(void); -void fuse_disable_power(void); -void fuse_wait_idle(void); - -/* Initialize the fuse driver */ -void fuse_init(void) { - /* Make all fuse registers visible, disable the private key and disable programming. */ - clkrst_enable_fuse_regs(true); - fuse_disable_private_key(); - fuse_disable_programming(); - - /* TODO: Should we allow this to be done later? */ - if (!g_has_checked_for_rcm_bug_patch) { - (void)(fuse_has_rcm_bug_patch()); - } -} - -/* Disable access to the private key and set the TZ sticky bit. */ -void fuse_disable_private_key(void) { - FUSE_REGS->FUSE_PRIVATEKEYDISABLE = 0x10; -} - -/* Disables all fuse programming. */ -void fuse_disable_programming(void) { - FUSE_REGS->FUSE_DISABLEREGPROGRAM = 1; -} - -/* Enable power to the fuse hardware array. */ -void fuse_enable_power(void) { - APBDEV_PMC_FUSE_CTRL &= ~(0x200); /* Clear PMC_FUSE_CTRL_PS18_LATCH_CLEAR. */ - mdelay(1); - APBDEV_PMC_FUSE_CTRL |= 0x100; /* Set PMC_FUSE_CTRL_PS18_LATCH_SET. */ - mdelay(1); -} - -/* Disable power to the fuse hardware array. */ -void fuse_disable_power(void) { - APBDEV_PMC_FUSE_CTRL &= ~(0x100); /* Clear PMC_FUSE_CTRL_PS18_LATCH_SET. */ - mdelay(1); - APBDEV_PMC_FUSE_CTRL |= 0x200; /* Set PMC_FUSE_CTRL_PS18_LATCH_CLEAR. */ - mdelay(1); -} - -/* Wait for the fuse driver to go idle. */ -void fuse_wait_idle(void) { - uint32_t ctrl_val = 0; - - /* Wait for STATE_IDLE */ - while ((ctrl_val & (0xF0000)) != 0x40000) - ctrl_val = FUSE_REGS->FUSE_FUSECTRL; -} - -/* Read a fuse from the hardware array. */ -uint32_t fuse_hw_read(uint32_t addr) { - /* Wait for idle state. */ - fuse_wait_idle(); - - /* Program the target address. */ - FUSE_REGS->FUSE_FUSEADDR = addr; - - /* Enable read operation in control register. */ - uint32_t ctrl_val = FUSE_REGS->FUSE_FUSECTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x1; /* Set READ command. */ - FUSE_REGS->FUSE_FUSECTRL = ctrl_val; - - /* Wait for idle state. */ - fuse_wait_idle(); - - return FUSE_REGS->FUSE_FUSERDATA; -} - -/* Write a fuse in the hardware array. */ -void fuse_hw_write(uint32_t value, uint32_t addr) { - /* Wait for idle state. */ - fuse_wait_idle(); - - /* Program the target address and value. */ - FUSE_REGS->FUSE_FUSEADDR = addr; - FUSE_REGS->FUSE_FUSEWDATA = value; - - /* Enable write operation in control register. */ - uint32_t ctrl_val = FUSE_REGS->FUSE_FUSECTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x2; /* Set WRITE command. */ - FUSE_REGS->FUSE_FUSECTRL = ctrl_val; - - /* Wait for idle state. */ - fuse_wait_idle(); -} - -/* Sense the fuse hardware array into the shadow cache. */ -void fuse_hw_sense(void) { - /* Wait for idle state. */ - fuse_wait_idle(); - - /* Enable sense operation in control register */ - uint32_t ctrl_val = FUSE_REGS->FUSE_FUSECTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x3; /* Set SENSE_CTRL command */ - FUSE_REGS->FUSE_FUSECTRL = ctrl_val; - - /* Wait for idle state. */ - fuse_wait_idle(); -} - -/* Read the SKU info register from the shadow cache. */ -uint32_t fuse_get_sku_info(void) { - return FUSE_CHIP_REGS->FUSE_SKU_INFO; -} - -/* Read the bootrom patch version from a register in the shadow cache. */ -uint32_t fuse_get_bootrom_patch_version(void) { - return FUSE_CHIP_REGS->FUSE_SOC_SPEEDO_1_CALIB; -} - -/* Read a spare bit register from the shadow cache */ -uint32_t fuse_get_spare_bit(uint32_t idx) { - if (idx < 32) { - return FUSE_CHIP_REGS->FUSE_SPARE_BIT[idx]; - } else { - return 0; - } -} - -/* Read a reserved ODM register from the shadow cache. */ -uint32_t fuse_get_reserved_odm(uint32_t idx) { - if (idx < 8) { - return FUSE_CHIP_REGS->FUSE_RESERVED_ODM[idx]; - } else { - return 0; - } -} - -/* Get the DRAM ID using values in the shadow cache. */ -uint32_t fuse_get_dram_id(void) { - return ((fuse_get_reserved_odm(4) >> 3) & 0x7); -} - -/* Derive the Device ID using values in the shadow cache. */ -uint64_t fuse_get_device_id(void) { - uint64_t device_id = 0; - uint64_t y_coord = FUSE_CHIP_REGS->FUSE_OPT_Y_COORDINATE & 0x1FF; - uint64_t x_coord = FUSE_CHIP_REGS->FUSE_OPT_X_COORDINATE & 0x1FF; - uint64_t wafer_id = FUSE_CHIP_REGS->FUSE_OPT_WAFER_ID & 0x3F; - uint32_t lot_code = FUSE_CHIP_REGS->FUSE_OPT_LOT_CODE_0; - uint64_t fab_code = FUSE_CHIP_REGS->FUSE_OPT_FAB_CODE & 0x3F; - - uint64_t derived_lot_code = 0; - for (unsigned int i = 0; i < 5; i++) { - derived_lot_code = (derived_lot_code * 0x24) + ((lot_code >> (24 - 6*i)) & 0x3F); - } - derived_lot_code &= 0x03FFFFFF; - - device_id |= y_coord << 0; - device_id |= x_coord << 9; - device_id |= wafer_id << 18; - device_id |= derived_lot_code << 24; - device_id |= fab_code << 50; - - return device_id; -} - -/* Derive the Hardware Type using values in the shadow cache. */ -uint32_t fuse_get_hardware_type(uint32_t target_firmware) { - uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); - uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); - - /* Firmware from versions 1.0.0 to 3.0.2. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); - if (hardware_type >= 1) { - return (hardware_type > 2) ? 3 : hardware_type - 1; - } else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { - return 0; - } else { - return 3; - } - } else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */ - static const uint32_t types[] = {0,1,4,3}; - hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); - hardware_type--; - return (hardware_type > 3) ? 4 : types[hardware_type]; - } else { /* Firmware versions from 7.0.0 onwards. */ - /* Always return 0 in retail. */ - return 0; - } -} - -/* Derive the Retail Type using values in the shadow cache. */ -uint32_t fuse_get_retail_type(void) { - /* Retail Type = IS_RETAIL | UNIT_TYPE. */ - uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); - uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); - if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */ - return 1; - } else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ - return 0; - } - return 2; /* IS_RETAIL | DEV_UNIT */ -} - -/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ -void fuse_get_hardware_info(void *dst) { - uint32_t hw_info[0x4]; - - uint32_t ops_reserved = FUSE_CHIP_REGS->FUSE_OPT_OPS_RESERVED & 0x3F; - uint32_t y_coord = FUSE_CHIP_REGS->FUSE_OPT_Y_COORDINATE & 0x1FF; - uint32_t x_coord = FUSE_CHIP_REGS->FUSE_OPT_X_COORDINATE & 0x1FF; - uint32_t wafer_id = FUSE_CHIP_REGS->FUSE_OPT_WAFER_ID & 0x3F; - uint32_t lot_code_0 = FUSE_CHIP_REGS->FUSE_OPT_LOT_CODE_0; - uint32_t lot_code_1 = FUSE_CHIP_REGS->FUSE_OPT_LOT_CODE_1 & 0x0FFFFFFF; - uint32_t fab_code = FUSE_CHIP_REGS->FUSE_OPT_FAB_CODE & 0x3F; - uint32_t vendor_code = FUSE_CHIP_REGS->FUSE_OPT_VENDOR_CODE & 0xF; - - /* Hardware Info = OPS_RESERVED || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID */ - hw_info[0] = (uint32_t)((lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | (ops_reserved)); - hw_info[1] = (uint32_t)((lot_code_0 << 26) | (lot_code_1 >> 2)); - hw_info[2] = (uint32_t)((fab_code << 26) | (lot_code_0 >> 6)); - hw_info[3] = (uint32_t)(vendor_code); - - memcpy(dst, hw_info, 0x10); -} - -/* Get the Key Generation value. */ -uint32_t fuse_get_5x_key_generation(void) { - if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) { - return (fuse_get_reserved_odm(2) & 0x1F); - } else { - return 0; - } -} - -/* Returns the fuse version expected for the firmware. */ -uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware) { - if (fuse_get_retail_type() != 0) { - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { - return 13; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_1_0) { - return 12; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0) { - return 11; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { - return 10; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { - return 9; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { - return 8; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - return 7; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - return 6; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - return 5; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_2) { - return 4; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - return 3; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - return 2; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { - return 1; - } else { - return 0; - } - } else { - return (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) ? 1 : 0; - } -} - -/* Check for RCM bug patches. */ -bool fuse_has_rcm_bug_patch(void) { - /* Only check for RCM bug patch once, and cache our result. */ - if (!g_has_checked_for_rcm_bug_patch) { - /* Some patched units use XUSB in RCM. */ - if (FUSE_CHIP_REGS->FUSE_RESERVED_SW & 0x80) { - g_has_rcm_bug_patch = true; - } - - /* Other units have a proper ipatch instead. */ - { - uint32_t word_count = FUSE_CHIP_REGS->FUSE_FIRST_BOOTROM_PATCH_SIZE & 0x7f; - uint32_t word_addr = 191; - - while (word_count) { - uint32_t word0 = fuse_hw_read(word_addr); - uint32_t ipatch_count = (word0 >> 16) & 0xf; - - for (uint32_t i = 0; i < ipatch_count; i++) { - uint32_t word = fuse_hw_read(word_addr - (i + 1)); - uint32_t addr = (word >> 16) * 2; - - if (addr == 0x769a) { - g_has_rcm_bug_patch = true; - } - } - - word_addr -= word_count; - word_count = word0 >> 25; - } - } - } - g_has_checked_for_rcm_bug_patch = true; - - return g_has_rcm_bug_patch; -} \ No newline at end of file diff --git a/exosphere/src/fuse.h b/exosphere/src/fuse.h deleted file mode 100644 index 931f7ea08..000000000 --- a/exosphere/src/fuse.h +++ /dev/null @@ -1,234 +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_FUSE_H -#define EXOSPHERE_FUSE_H - -#include <stdbool.h> -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 FUSE registers. */ - -typedef struct { - uint32_t FUSE_FUSECTRL; - uint32_t FUSE_FUSEADDR; - uint32_t FUSE_FUSERDATA; - uint32_t FUSE_FUSEWDATA; - uint32_t FUSE_FUSETIME_RD1; - uint32_t FUSE_FUSETIME_RD2; - uint32_t FUSE_FUSETIME_PGM1; - uint32_t FUSE_FUSETIME_PGM2; - uint32_t FUSE_PRIV2INTFC_START; - uint32_t FUSE_FUSEBYPASS; - uint32_t FUSE_PRIVATEKEYDISABLE; - uint32_t FUSE_DISABLEREGPROGRAM; - uint32_t FUSE_WRITE_ACCESS_SW; - uint32_t FUSE_PWR_GOOD_SW; - uint32_t _0x38; - uint32_t FUSE_PRIV2RESHIFT; - uint32_t _0x40[0x3]; - uint32_t FUSE_FUSETIME_RD3; - uint32_t _0x50[0xC]; - uint32_t FUSE_PRIVATE_KEY0_NONZERO; - uint32_t FUSE_PRIVATE_KEY1_NONZERO; - uint32_t FUSE_PRIVATE_KEY2_NONZERO; - uint32_t FUSE_PRIVATE_KEY3_NONZERO; - uint32_t FUSE_PRIVATE_KEY4_NONZERO; - uint32_t _0x90[0x1C]; -} tegra_fuse_t; - -typedef struct { - uint32_t FUSE_PRODUCTION_MODE; - uint32_t FUSE_JTAG_SECUREID_VALID; - uint32_t FUSE_ODM_LOCK; - uint32_t FUSE_OPT_OPENGL_EN; - uint32_t FUSE_SKU_INFO; - uint32_t FUSE_CPU_SPEEDO_0_CALIB; - uint32_t FUSE_CPU_IDDQ_CALIB; - uint32_t FUSE_DAC_CRT_CALIB; - uint32_t FUSE_DAC_HDTV_CALIB; - uint32_t FUSE_DAC_SDTV_CALIB; - uint32_t FUSE_OPT_FT_REV; - uint32_t FUSE_CPU_SPEEDO_1_CALIB; - uint32_t FUSE_CPU_SPEEDO_2_CALIB; - uint32_t FUSE_SOC_SPEEDO_0_CALIB; - uint32_t FUSE_SOC_SPEEDO_1_CALIB; - uint32_t FUSE_SOC_SPEEDO_2_CALIB; - uint32_t FUSE_SOC_IDDQ_CALIB; - uint32_t FUSE_RESERVED_PRODUCTION_WP; - uint32_t FUSE_FA; - uint32_t FUSE_RESERVED_PRODUCTION; - uint32_t FUSE_HDMI_LANE0_CALIB; - uint32_t FUSE_HDMI_LANE1_CALIB; - uint32_t FUSE_HDMI_LANE2_CALIB; - uint32_t FUSE_HDMI_LANE3_CALIB; - uint32_t FUSE_ENCRYPTION_RATE; - uint32_t FUSE_PUBLIC_KEY[0x8]; - uint32_t FUSE_TSENSOR1_CALIB; - uint32_t FUSE_TSENSOR2_CALIB; - uint32_t FUSE_VSENSOR_CALIB; - uint32_t FUSE_OPT_CP_REV; - uint32_t FUSE_OPT_PFG; - uint32_t FUSE_TSENSOR0_CALIB; - uint32_t FUSE_FIRST_BOOTROM_PATCH_SIZE; - uint32_t FUSE_SECURITY_MODE; - uint32_t FUSE_PRIVATE_KEY[0x5]; - uint32_t FUSE_ARM_JTAG_DIS; - uint32_t FUSE_BOOT_DEVICE_INFO; - uint32_t FUSE_RESERVED_SW; - uint32_t FUSE_OPT_VP9_DISABLE; - uint32_t FUSE_RESERVED_ODM[0x8]; - uint32_t FUSE_OBS_DIS; - uint32_t FUSE_NOR_INFO; - uint32_t FUSE_USB_CALIB; - uint32_t FUSE_SKU_DIRECT_CONFIG; - uint32_t FUSE_KFUSE_PRIVKEY_CTRL; - uint32_t FUSE_PACKAGE_INFO; - uint32_t FUSE_OPT_VENDOR_CODE; - uint32_t FUSE_OPT_FAB_CODE; - uint32_t FUSE_OPT_LOT_CODE_0; - uint32_t FUSE_OPT_LOT_CODE_1; - uint32_t FUSE_OPT_WAFER_ID; - uint32_t FUSE_OPT_X_COORDINATE; - uint32_t FUSE_OPT_Y_COORDINATE; - uint32_t FUSE_OPT_SEC_DEBUG_EN; - uint32_t FUSE_OPT_OPS_RESERVED; - uint32_t FUSE_SATA_CALIB; - uint32_t FUSE_GPU_IDDQ_CALIB; - uint32_t FUSE_TSENSOR3_CALIB; - uint32_t FUSE_SKU_BOND_OUT_L; - uint32_t FUSE_SKU_BOND_OUT_H; - uint32_t FUSE_SKU_BOND_OUT_U; - uint32_t FUSE_SKU_BOND_OUT_V; - uint32_t FUSE_SKU_BOND_OUT_W; - uint32_t FUSE_OPT_SAMPLE_TYPE; - uint32_t FUSE_OPT_SUBREVISION; - uint32_t FUSE_OPT_SW_RESERVED_0; - uint32_t FUSE_OPT_SW_RESERVED_1; - uint32_t FUSE_TSENSOR4_CALIB; - uint32_t FUSE_TSENSOR5_CALIB; - uint32_t FUSE_TSENSOR6_CALIB; - uint32_t FUSE_TSENSOR7_CALIB; - uint32_t FUSE_OPT_PRIV_SEC_EN; - uint32_t FUSE_PKC_DISABLE; - uint32_t _0x16C; - uint32_t _0x170; - uint32_t _0x174; - uint32_t _0x178; - uint32_t FUSE_FUSE2TSEC_DEBUG_DISABLE; - uint32_t FUSE_TSENSOR_COMMON; - uint32_t FUSE_OPT_CP_BIN; - uint32_t FUSE_OPT_GPU_DISABLE; - uint32_t FUSE_OPT_FT_BIN; - uint32_t FUSE_OPT_DONE_MAP; - uint32_t _0x194; - uint32_t FUSE_APB2JTAG_DISABLE; - uint32_t FUSE_ODM_INFO; - uint32_t _0x1A0; - uint32_t _0x1A4; - uint32_t FUSE_ARM_CRYPT_DE_FEATURE; - uint32_t _0x1AC; - uint32_t _0x1B0; - uint32_t _0x1B4; - uint32_t _0x1B8; - uint32_t _0x1BC; - uint32_t FUSE_WOA_SKU_FLAG; - uint32_t FUSE_ECO_RESERVE_1; - uint32_t FUSE_GCPLEX_CONFIG_FUSE; - uint32_t FUSE_PRODUCTION_MONTH; - uint32_t FUSE_RAM_REPAIR_INDICATOR; - uint32_t FUSE_TSENSOR9_CALIB; - uint32_t _0x1D8; - uint32_t FUSE_VMIN_CALIBRATION; - uint32_t FUSE_AGING_SENSOR_CALIBRATION; - uint32_t FUSE_DEBUG_AUTHENTICATION; - uint32_t FUSE_SECURE_PROVISION_INDEX; - uint32_t FUSE_SECURE_PROVISION_INFO; - uint32_t FUSE_OPT_GPU_DISABLE_CP1; - uint32_t FUSE_SPARE_ENDIS; - uint32_t FUSE_ECO_RESERVE_0; - uint32_t _0x1FC; - uint32_t _0x200; - uint32_t FUSE_RESERVED_CALIB0; - uint32_t FUSE_RESERVED_CALIB1; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE_CP1; - uint32_t FUSE_OPT_CPU_DISABLE; - uint32_t FUSE_OPT_CPU_DISABLE_CP1; - uint32_t FUSE_TSENSOR10_CALIB; - uint32_t FUSE_TSENSOR10_CALIB_AUX; - uint32_t FUSE_OPT_RAM_SVOP_DP; - uint32_t FUSE_OPT_RAM_SVOP_PDP; - uint32_t FUSE_OPT_RAM_SVOP_REG; - uint32_t FUSE_OPT_RAM_SVOP_SP; - uint32_t FUSE_OPT_RAM_SVOP_SMPDP; - uint32_t FUSE_OPT_GPU_TPC0_DISABLE_CP2; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE_CP1; - uint32_t FUSE_OPT_GPU_TPC1_DISABLE_CP2; - uint32_t FUSE_OPT_CPU_DISABLE_CP2; - uint32_t FUSE_OPT_GPU_DISABLE_CP2; - uint32_t FUSE_USB_CALIB_EXT; - uint32_t FUSE_RESERVED_FIELD; - uint32_t FUSE_OPT_ECC_EN; - uint32_t _0x25C; - uint32_t _0x260; - uint32_t _0x264; - uint32_t _0x268; - uint32_t _0x26C; - uint32_t _0x270; - uint32_t _0x274; - uint32_t _0x278; - uint32_t FUSE_SPARE_REALIGNMENT_REG; - uint32_t FUSE_SPARE_BIT[0x20]; -} tegra_fuse_chip_t; - -static inline volatile tegra_fuse_t *fuse_get_regs(void) { - return (volatile tegra_fuse_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_FUSE) + 0x800); -} - -static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void) { - return (volatile tegra_fuse_chip_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_FUSE) + 0x900); -} - -#define FUSE_REGS (fuse_get_regs()) -#define FUSE_CHIP_REGS (fuse_chip_get_regs()) - -void fuse_init(void); -void fuse_disable_programming(void); -void fuse_disable_private_key(void); - -uint32_t fuse_get_sku_info(void); -uint32_t fuse_get_spare_bit(uint32_t idx); -uint32_t fuse_get_reserved_odm(uint32_t idx); -uint32_t fuse_get_bootrom_patch_version(void); -uint64_t fuse_get_device_id(void); -uint32_t fuse_get_dram_id(void); -uint32_t fuse_get_hardware_type(uint32_t target_firmware); -uint32_t fuse_get_retail_type(void); -void fuse_get_hardware_info(void *dst); -uint32_t fuse_get_5x_key_generation(void); -bool fuse_has_rcm_bug_patch(void); - -uint32_t fuse_hw_read(uint32_t addr); -void fuse_hw_write(uint32_t value, uint32_t addr); -void fuse_hw_sense(void); - - -uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware); - -#endif diff --git a/exosphere/src/gcm.c b/exosphere/src/gcm.c deleted file mode 100644 index 239093c34..000000000 --- a/exosphere/src/gcm.c +++ /dev/null @@ -1,229 +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 <string.h> - -#include "arm.h" -#include "utils.h" -#include "fuse.h" -#include "gcm.h" - -#include "sealedkeys.h" -#include "se.h" - -/* Shifts right a little endian 128-bit value. */ -static void shr_128(uint64_t *val) { - val[0] >>= 1; - val[0] |= (val[1] & 1) << 63; - val[1] >>= 1; -} - -/* Shifts left a little endian 128-bit value. */ -static void shl_128(uint64_t *val) { - val[1] <<= 1; - val[1] |= (val[0] & (1ull << 63)) >> 63; - val[0] <<= 1; -} - -/* Multiplies two 128-bit numbers X,Y in the GF(128) Galois Field. */ -static void gf128_mul(uint8_t *dst, const uint8_t *x, const uint8_t *y) { - uint8_t x_work[0x10]; - uint8_t y_work[0x10]; - uint8_t dst_work[0x10]; - - uint64_t *p_x = (uint64_t *)(&x_work[0]); - uint64_t *p_y = (uint64_t *)(&y_work[0]); - uint64_t *p_dst = (uint64_t *)(&dst_work[0]); - - /* Initialize buffers. */ - for (unsigned int i = 0; i < 0x10; i++) { - x_work[i] = x[0xF-i]; - y_work[i] = y[0xF-i]; - dst_work[i] = 0; - } - - /* Perform operation for each bit in y. */ - for (unsigned int round = 0; round < 0x80; round++) { - p_dst[0] ^= p_x[0] * ((y_work[0xF] & 0x80) >> 7); - p_dst[1] ^= p_x[1] * ((y_work[0xF] & 0x80) >> 7); - shl_128(p_y); - uint8_t xval = 0xE1 * (x_work[0] & 1); - shr_128(p_x); - x_work[0xF] ^= xval; - } - - for (unsigned int i = 0; i < 0x10; i++) { - dst[i] = dst_work[0xF-i]; - } -} - - - -/* Performs an AES-GCM GHASH operation over the data into dst. */ -static void ghash(void *dst, const void *data, size_t data_size, const void *j_block, bool encrypt) { - uint8_t x[0x10] = {0}; - uint8_t h[0x10]; - - uint64_t *p_x = (uint64_t *)(&x[0]); - uint64_t *p_data = (uint64_t *)data; - - /* H = aes_ecb_encrypt(zeroes) */ - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, x, 0x10); - - size_t total_size = data_size; - - while (data_size >= 0x10) { - /* X = (X ^ current_block) * H */ - p_x[0] ^= p_data[0]; - p_x[1] ^= p_data[1]; - gf128_mul(x, x, h); - - /* Increment p_data by 0x10 bytes. */ - p_data += 2; - data_size -= 0x10; - } - - /* Nintendo's code *discards all data in the last block* if unaligned. */ - /* And treats that block as though it were all-zero. */ - /* This is a bug, they just forget to XOR with the copy of the last block they save. */ - if (data_size & 0xF) { - gf128_mul(x, x, h); - } - - uint64_t xor_size = total_size << 3; - xor_size = __builtin_bswap64(xor_size); - - /* Due to a Nintendo bug, the wrong QWORD gets XOR'd in the "final output block" case. */ - if (encrypt) { - p_x[0] ^= xor_size; - } else { - p_x[1] ^= xor_size; - } - - gf128_mul(x, x, h); - - /* If final output block, XOR with encrypted J block. */ - if (encrypt) { - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, j_block, 0x10); - for (unsigned int i = 0; i < 0x10; i++) { - x[i] ^= h[i]; - } - } - /* Copy output. */ - memcpy(dst, x, 0x10); -} - - -/* This function is a doozy. It decrypts and validates a (non-standard) AES-GCM wrapped keypair. */ -size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, bool is_personalized, uint8_t *out_deviceid_high) { - if (is_personalized == 0) { - /* Devkit keys use a different keyformat without a MAC/Device ID. */ - if (src_size <= 0x10 || src_size - 0x10 > dst_size) { - generic_panic(); - } - } else { - if (src_size <= 0x30 || src_size - 0x20 > dst_size) { - generic_panic(); - } - } - - uint8_t intermediate_buf[0x400] = {0}; - - /* Unwrap the key */ - unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size); - - /* Decrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */ - se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, intermediate_buf, dst_size, src + 0x10, src_size - 0x10, src, 0x10); - - if (!is_personalized) { - /* Devkit non-personalized keys have no further authentication. */ - memcpy(dst, intermediate_buf, src_size - 0x10); - memset(intermediate_buf, 0, sizeof(intermediate_buf)); - return src_size - 0x10; - } - - /* J = GHASH(CTR); */ - uint8_t j_block[0x10]; - ghash(j_block, src, 0x10, NULL, false); - - /* MAC = GHASH(PLAINTEXT) ^ ENCRYPT(J) */ - /* Note: That MAC is calculated over plaintext is non-standard. */ - /* It is supposed to be over the ciphertext. */ - uint8_t calc_mac[0x10]; - ghash(calc_mac, intermediate_buf, src_size - 0x20, j_block, true); - - /* Const-time memcmp. */ - const uint8_t *src_bytes = src; - int different = 0; - for (unsigned int i = 0; i < 0x10; i++) { - different |= src_bytes[src_size - 0x10 + i] ^ calc_mac[i]; - } - if (different) { - return 0; - } - - if ((read64be(intermediate_buf, src_size - 0x28) & 0x00FFFFFFFFFFFFFFULL) != fuse_get_device_id()) { - return 0; - } - - if (out_deviceid_high != NULL) { - *out_deviceid_high = intermediate_buf[src_size - 0x28]; - } - - memcpy(dst, intermediate_buf, src_size - 0x30); - memset(intermediate_buf, 0, sizeof(intermediate_buf)); - return src_size - 0x30; -} - -void gcm_encrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, uint64_t deviceid_high) { - uint8_t intermediate_buf[0x400] = {0}; - if (src_size + 0x30 > dst_size) { - generic_panic(); - } - - /* Unwrap the key */ - unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size); - - /* Generate a random CTR. */ - flush_dcache_range(intermediate_buf, intermediate_buf + 0x10); - se_generate_random(KEYSLOT_SWITCH_RNGKEY, intermediate_buf, 0x10); - flush_dcache_range(intermediate_buf, intermediate_buf + 0x10); - - /* Copy in the src. */ - memcpy(intermediate_buf + 0x10, src, src_size); - - /* Write Device ID. */ - write64be(intermediate_buf, src_size + 0x18, fuse_get_device_id() | (deviceid_high << 56)); - - /* J = GHASH(CTR); */ - uint8_t j_block[0x10]; - ghash(j_block, intermediate_buf, 0x10, NULL, false); - - /* MAC = GHASH(PLAINTEXT) ^ ENCRYPT(J) */ - /* Note: That MAC is calculated over plaintext is non-standard. */ - /* It is supposed to be over the ciphertext. */ - ghash(intermediate_buf + src_size + 0x20, intermediate_buf + 0x10, src_size + 0x10, j_block, true); - - /* Encrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */ - se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, intermediate_buf + 0x10, src_size + 0x10, intermediate_buf + 0x10, src_size + 0x10, intermediate_buf, 0x10); - - /* Copy the wrapped key out. */ - memcpy(dst, intermediate_buf, src_size + 0x30); - memset(intermediate_buf, 0, sizeof(intermediate_buf)); -} diff --git a/exosphere/src/gcm.h b/exosphere/src/gcm.h deleted file mode 100644 index 4fe491532..000000000 --- a/exosphere/src/gcm.h +++ /dev/null @@ -1,37 +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_GCM_H -#define EXOSPHERE_GCM_H - -#include <stdbool.h> -#include <stdint.h> - -size_t gcm_decrypt_key(void *dst, size_t dst_size, - const void *src, size_t src_size, - const void *sealed_kek, size_t kek_size, - const void *wrapped_key, size_t key_size, - unsigned int usecase, bool is_personalized, - uint8_t *out_deviceid_high); - - -void gcm_encrypt_key(void *dst, size_t dst_size, - const void *src, size_t src_size, - const void *sealed_kek, size_t kek_size, - const void *wrapped_key, size_t key_size, - unsigned int usecase, uint64_t deviceid_high); - -#endif diff --git a/exosphere/src/i2c.c b/exosphere/src/i2c.c deleted file mode 100644 index 281cc6e96..000000000 --- a/exosphere/src/i2c.c +++ /dev/null @@ -1,252 +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 "i2c.h" -#include "utils.h" -#include "timers.h" -#include "pinmux.h" - -/* Prototypes for internal commands. */ -volatile tegra_i2c_t *i2c_get_registers_from_id(I2CDevice id); -void i2c_load_config(volatile tegra_i2c_t *regs); - -bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_size); -bool i2c_send(I2CDevice id, uint8_t device, uint8_t r, void *src, size_t src_size); - -bool i2c_write(volatile tegra_i2c_t *regs, uint8_t device, void *src, size_t src_size); -bool i2c_read(volatile tegra_i2c_t *regs, uint8_t device, void *dst, size_t dst_size); - -/* Configure I2C pinmux. */ -void i2c_config(I2CDevice id) { - volatile tegra_pinmux_t *pinmux = pinmux_get_regs(); - - switch (id) { - case I2C_1: - pinmux->gen1_i2c_scl = PINMUX_INPUT; - pinmux->gen1_i2c_sda = PINMUX_INPUT; - break; - case I2C_2: - pinmux->gen2_i2c_scl = PINMUX_INPUT; - pinmux->gen2_i2c_sda = PINMUX_INPUT; - break; - case I2C_3: - pinmux->gen3_i2c_scl = PINMUX_INPUT; - pinmux->gen3_i2c_sda = PINMUX_INPUT; - break; - case I2C_4: - pinmux->cam_i2c_scl = PINMUX_INPUT; - pinmux->cam_i2c_sda = PINMUX_INPUT; - break; - case I2C_5: - pinmux->pwr_i2c_scl = PINMUX_INPUT; - pinmux->pwr_i2c_sda = PINMUX_INPUT; - break; - case I2C_6: - /* Unused. */ - break; - default: break; - } -} - -/* Initialize I2C based on registers. */ -void i2c_init(I2CDevice id) { - volatile tegra_i2c_t *regs = i2c_get_registers_from_id(id); - - /* Setup divisor, and clear the bus. */ - regs->I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001; - regs->I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003; - - /* Load hardware configuration. */ - i2c_load_config(regs); - - /* Wait a while until BUS_CLEAR_DONE is set. */ - for (unsigned int i = 0; i < 10; i++) { - wait(25); - if (regs->I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) { - break; - } - } - - /* Read the BUS_CLEAR_STATUS. Result doesn't matter. */ - regs->I2C_I2C_BUS_CLEAR_STATUS_0; - - /* Read and set the Interrupt Status. */ - uint32_t int_status = regs->I2C_INTERRUPT_STATUS_REGISTER_0; - regs->I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; -} - -/* Sets a bit in a PMIC register over I2C during CPU shutdown. */ -void i2c_send_pmic_cpu_shutdown_cmd(void) { - uint32_t val = 0; - /* PMIC == Device 4:3C. */ - i2c_query(I2C_5, MAX77620_PWR_I2C_ADDR, 0x41, &val, 1); - val |= 4; - i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, 0x41, &val, 1); -} - -/* Queries the value of TI charger bit over I2C. */ -bool i2c_query_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(I2C_1, BQ24193_I2C_ADDR, 0, &val, 1); - return (val & 0x80) != 0; -} - -/* Clears TI charger bit over I2C. */ -void i2c_clear_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(I2C_1, BQ24193_I2C_ADDR, 0, &val, 1); - val &= 0x7F; - i2c_send(I2C_1, BQ24193_I2C_ADDR, 0, &val, 1); -} - -/* Sets TI charger bit over I2C. */ -void i2c_set_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(I2C_1, BQ24193_I2C_ADDR, 0, &val, 1); - val |= 0x80; - i2c_send(I2C_1, BQ24193_I2C_ADDR, 0, &val, 1); -} - -/* Get registers pointer based on I2C ID. */ -volatile tegra_i2c_t *i2c_get_registers_from_id(I2CDevice id) { - switch (id) { - case I2C_1: - return I2C1_REGS; - case I2C_2: - return I2C2_REGS; - case I2C_3: - return I2C3_REGS; - case I2C_4: - return I2C4_REGS; - case I2C_5: - return I2C5_REGS; - case I2C_6: - return I2C6_REGS; - default: - generic_panic(); - } - return NULL; -} - -/* Load hardware config for I2C4. */ -void i2c_load_config(volatile tegra_i2c_t *regs) { - /* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */ - regs->I2C_I2C_CONFIG_LOAD_0 = 0x25; - - /* Wait a bit for master config to be loaded. */ - for (unsigned int i = 0; i < 20; i++) { - wait(1); - if (!(regs->I2C_I2C_CONFIG_LOAD_0 & 1)) { - break; - } - } -} - -/* Reads a register from a device over I2C, writes result to output. */ -bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_size) { - volatile tegra_i2c_t *regs = i2c_get_registers_from_id(id); - uint32_t val = r; - - /* Write single byte register ID to device. */ - if (!i2c_write(regs, device, &val, 1)) { - return false; - } - /* Limit output size to 32-bits. */ - if (dst_size > 4) { - return false; - } - - return i2c_read(regs, device, dst, dst_size); -} - -/* Writes a value to a register over I2C. */ -bool i2c_send(I2CDevice id, uint8_t device, uint8_t r, void *src, size_t src_size) { - uint32_t val = r; - if (src_size == 0) { - return true; - } else if (src_size <= 3) { - memcpy(((uint8_t *)&val) + 1, src, src_size); - return i2c_write(i2c_get_registers_from_id(id), device, &val, src_size + 1); - } else { - return false; - } -} - -/* Writes bytes to device over I2C. */ -bool i2c_write(volatile tegra_i2c_t *regs, uint8_t device, void *src, size_t src_size) { - if (src_size > 4) { - return false; - } else if (src_size == 0) { - return true; - } - - /* Set device for 7-bit write mode. */ - regs->I2C_I2C_CMD_ADDR0_0 = device << 1; - - /* Load in data to write. */ - regs->I2C_I2C_CMD_DATA1_0 = read32le(src, 0); - - /* Set config with LENGTH = src_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - regs->I2C_I2C_CNFG_0 = ((src_size << 1) - 2) | 0x2800; - - i2c_load_config(regs); - - /* Config |= SEND; */ - regs->I2C_I2C_CNFG_0 = ((regs->I2C_I2C_CNFG_0 & 0xFFFFFDFF) | 0x200); - - while (regs->I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */ - return (regs->I2C_I2C_STATUS_0 & 0xF) == 0; -} - -/* Reads bytes from device over I2C. */ -bool i2c_read(volatile tegra_i2c_t *regs, uint8_t device, void *dst, size_t dst_size) { - if (dst_size > 4) { - return false; - } else if (dst_size == 0) { - return true; - } - - /* Set device for 7-bit read mode. */ - regs->I2C_I2C_CMD_ADDR0_0 = (device << 1) | 1; - - /* Set config with LENGTH = dst_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - regs->I2C_I2C_CNFG_0 = ((dst_size << 1) - 2) | 0x2840; - - i2c_load_config(regs); - - /* Config |= SEND; */ - regs->I2C_I2C_CNFG_0 = ((regs->I2C_I2C_CNFG_0 & 0xFFFFFDFF) | 0x200); - - while (regs->I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Ensure success. */ - if ((regs->I2C_I2C_STATUS_0 & 0xF) != 0) { - return false; - } - - uint32_t val = regs->I2C_I2C_CMD_DATA1_0; - memcpy(dst, &val, dst_size); - return true; -} diff --git a/exosphere/src/i2c.h b/exosphere/src/i2c.h deleted file mode 100644 index eb004972a..000000000 --- a/exosphere/src/i2c.h +++ /dev/null @@ -1,113 +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_I2C_H -#define EXOSPHERE_I2C_H - -#include <stdbool.h> -#include <stdint.h> -#include <string.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 I2C registers. */ - -#define MAX77621_CPU_I2C_ADDR 0x1B -#define MAX77621_GPU_I2C_ADDR 0x1C -#define MAX17050_I2C_ADDR 0x36 -#define MAX77620_PWR_I2C_ADDR 0x3C -#define MAX77620_RTC_I2C_ADDR 0x68 -#define BQ24193_I2C_ADDR 0x6B - -typedef enum { - I2C_1 = 0, - I2C_2 = 1, - I2C_3 = 2, - I2C_4 = 3, - I2C_5 = 4, - I2C_6 = 5, -} I2CDevice; - -typedef struct { - uint32_t I2C_I2C_CNFG_0; - uint32_t I2C_I2C_CMD_ADDR0_0; - uint32_t I2C_I2C_CMD_ADDR1_0; - uint32_t I2C_I2C_CMD_DATA1_0; - uint32_t I2C_I2C_CMD_DATA2_0; - uint32_t _0x14; - uint32_t _0x18; - uint32_t I2C_I2C_STATUS_0; - uint32_t I2C_I2C_SL_CNFG_0; - uint32_t I2C_I2C_SL_RCVD_0; - uint32_t I2C_I2C_SL_STATUS_0; - uint32_t I2C_I2C_SL_ADDR1_0; - uint32_t I2C_I2C_SL_ADDR2_0; - uint32_t I2C_I2C_TLOW_SEXT_0; - uint32_t _0x38; - uint32_t I2C_I2C_SL_DELAY_COUNT_0; - uint32_t I2C_I2C_SL_INT_MASK_0; - uint32_t I2C_I2C_SL_INT_SOURCE_0; - uint32_t I2C_I2C_SL_INT_SET_0; - uint32_t _0x4C; - uint32_t I2C_I2C_TX_PACKET_FIFO_0; - uint32_t I2C_I2C_RX_FIFO_0; - uint32_t I2C_PACKET_TRANSFER_STATUS_0; - uint32_t I2C_FIFO_CONTROL_0; - uint32_t I2C_FIFO_STATUS_0; - uint32_t I2C_INTERRUPT_MASK_REGISTER_0; - uint32_t I2C_INTERRUPT_STATUS_REGISTER_0; - uint32_t I2C_I2C_CLK_DIVISOR_REGISTER_0; - uint32_t I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; - uint32_t I2C_I2C_INTERRUPT_SET_REGISTER_0; - uint32_t I2C_I2C_SLV_TX_PACKET_FIFO_0; - uint32_t I2C_I2C_SLV_RX_FIFO_0; - uint32_t I2C_I2C_SLV_PACKET_STATUS_0; - uint32_t I2C_I2C_BUS_CLEAR_CONFIG_0; - uint32_t I2C_I2C_BUS_CLEAR_STATUS_0; - uint32_t I2C_I2C_CONFIG_LOAD_0; - uint32_t _0x90; - uint32_t I2C_I2C_INTERFACE_TIMING_0_0; - uint32_t I2C_I2C_INTERFACE_TIMING_1_0; - uint32_t I2C_I2C_HS_INTERFACE_TIMING_0_0; - uint32_t I2C_I2C_HS_INTERFACE_TIMING_1_0; -} tegra_i2c_t; - -static inline uintptr_t get_i2c_dtv_234_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DTV_I2C234); -} - -static inline uintptr_t get_i2c56_spi2b_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_I2C56_SPI2B); -} - -#define I2C1_REGS ((volatile tegra_i2c_t *)(get_i2c_dtv_234_base() + 0x000)) -#define I2C2_REGS ((volatile tegra_i2c_t *)(get_i2c_dtv_234_base() + 0x400)) -#define I2C3_REGS ((volatile tegra_i2c_t *)(get_i2c_dtv_234_base() + 0x500)) -#define I2C4_REGS ((volatile tegra_i2c_t *)(get_i2c_dtv_234_base() + 0x700)) -#define I2C5_REGS ((volatile tegra_i2c_t *)(get_i2c56_spi2b_base() + 0x000)) -#define I2C6_REGS ((volatile tegra_i2c_t *)(get_i2c56_spi2b_base() + 0x100)) - -void i2c_config(I2CDevice id); - -void i2c_init(I2CDevice id); -bool i2c_query(I2CDevice id, uint8_t device, uint8_t r, void *dst, size_t dst_size); -bool i2c_send(I2CDevice id, uint8_t device, uint8_t r, void *src, size_t src_size); - -void i2c_send_pmic_cpu_shutdown_cmd(void); -bool i2c_query_ti_charger_bit_7(void); -void i2c_clear_ti_charger_bit_7(void); -void i2c_set_ti_charger_bit_7(void); - -#endif diff --git a/exosphere/src/interrupt.c b/exosphere/src/interrupt.c deleted file mode 100644 index 889ccdc95..000000000 --- a/exosphere/src/interrupt.c +++ /dev/null @@ -1,131 +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 <stdbool.h> - -#include "utils.h" -#include "interrupt.h" - -/* Global of registered handlers. */ -static struct { - unsigned int id; - void (*handler)(void); -} g_registered_interrupts[MAX_REGISTERED_INTERRUPTS] = { {0} }; - -static unsigned int get_interrupt_id(void) { - return GICC_IAR; -} - -/* Initializes the GIC. This must be called during wakeup. */ -void intr_initialize_gic(void) { - /* Setup interrupts 0-0x1F as nonsecure with highest non-secure priority. */ - GICD_IGROUPR[0] = 0xFFFFFFFF; - for (unsigned int i = 0; i < 0x20; i++) { - GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST_NONSECURE; - } - - /* Setup the GICC. */ - GICC_CTLR = 0x1D9; - GICC_PMR = GIC_PRI_HIGHEST_NONSECURE; - GICC_BPR = 7; -} - -/* Initializes Interrupt Groups 1-7 in the GIC. Called by pk2ldr. */ -void intr_initialize_gic_nonsecure(void) { - for (unsigned int i = 1; i < 8; i++) { - GICD_IGROUPR[i] = 0xFFFFFFFF; - } - - for (unsigned int i = 0x20; i < 0xE0; i++) { - GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST_NONSECURE; - } - GICD_CTLR = 1; -} - -/* Sets GICC_CTLR to appropriate pre-sleep value. */ -void intr_prepare_gicc_for_sleep(void) { - GICC_CTLR = 0x1E0; -} - -/* Sets an interrupt's group in the GICD. */ -void intr_set_group(unsigned int id, int group) { - GICD_IGROUPR[id >> 5] = (GICD_IGROUPR[id >> 5] & (~(1 << (id & 0x1F)))) | ((group & 1) << (id & 0x1F)); -} - -/* Sets an interrupt id as pending in the GICD. */ -void intr_set_pending(unsigned int id) { - GICD_ISPENDR[id >> 5] = 1 << (id & 0x1F); -} - -/* Sets an interrupt's priority in the GICD. */ -void intr_set_priority(unsigned int id, uint8_t priority) { - GICD_IPRIORITYR[id] = priority; -} - -/* Sets an interrupt's target CPU mask in the GICD. */ -void intr_set_cpu_mask(unsigned int id, uint8_t mask) { - GICD_ITARGETSR[id] = mask; -} - -/* Sets an interrupt's edge/level bits in the GICD. */ -void intr_set_edge_level(unsigned int id, int edge_level) { - GICD_ICFGR[id >> 4] = GICD_ICFGR[id >> 4] & ((~(3 << ((id & 0xF) << 1))) | (((edge_level & 1) << 1) << ((id & 0xF) << 1))); -} - -/* Sets an interrupt's enabled status in the GICD. */ -void intr_set_enabled(unsigned int id, int enabled) { - GICD_ISENABLER[id >> 5] = (enabled & 1) << (id & 0x1F); -} - -/* To be called by FIQ handler. */ -void handle_registered_interrupt(void) { - unsigned int interrupt_id = get_interrupt_id(); - if (interrupt_id <= 0xDF) { - bool found_handler = false; - for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { - if (g_registered_interrupts[i].id == interrupt_id) { - found_handler = true; - g_registered_interrupts[i].handler(); - /* Mark that interrupt is done. */ - GICC_EOIR = interrupt_id; - break; - } - } - /* We must have found a handler, or something went wrong. */ - if (!found_handler) { - generic_panic(); - } - } -} - -/* Registers an interrupt into the global. */ -void intr_register_handler(unsigned int id, void (*handler)(void)) { - bool registered_handler = false; - for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { - if (g_registered_interrupts[i].id == 0) { - g_registered_interrupts[i].handler = handler; - g_registered_interrupts[i].id = id; - registered_handler = true; - break; - } - } - /* Failure to register is an error condition. */ - if (!registered_handler) { - generic_panic(); - } -} - diff --git a/exosphere/src/interrupt.h b/exosphere/src/interrupt.h deleted file mode 100644 index 86cfd2c0a..000000000 --- a/exosphere/src/interrupt.h +++ /dev/null @@ -1,80 +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_INTERRUPT_H -#define EXOSPHERE_INTERRUPT_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 GIC-400 registers. */ - - -#define MAX_REGISTERED_INTERRUPTS 4 -#define INTERRUPT_ID_SECURITY_ENGINE 0x5A -#define INTERRUPT_ID_ACTIVITY_MONITOR_4X 0x4D -#define INTERRUPT_ID_1C 0x1C -#define INTERRUPT_ID_USER_SECURITY_ENGINE 0x2C - -static inline uintptr_t get_gicd_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_GICD); -} - -static inline uintptr_t get_gicc_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_GICC); -} - -#define GICD_BASE (get_gicd_base()) -#define GICC_BASE (get_gicc_base()) - -#define GICD_CTLR MAKE_REG32(GICD_BASE + 0x000ull) -#define GICD_IGROUPR ((volatile uint32_t *)(GICD_BASE + 0x080ull)) -#define GICD_ISENABLER ((volatile uint32_t *)(GICD_BASE + 0x100ull)) -#define GICD_ISPENDR ((volatile uint32_t *)(GICD_BASE + 0x200ull)) -#define GICD_IPRIORITYR ((volatile uint8_t *)(GICD_BASE + 0x400ull)) -#define GICD_ITARGETSR ((volatile uint8_t *)(GICD_BASE + 0x800ull)) -#define GICD_ICFGR ((volatile uint32_t *)(GICD_BASE + 0xC00ull)) - -#define GICC_CTLR MAKE_REG32(GICC_BASE + 0x0000ull) -#define GICC_PMR MAKE_REG32(GICC_BASE + 0x0004ull) -#define GICC_BPR MAKE_REG32(GICC_BASE + 0x0008ull) -#define GICC_IAR MAKE_REG32(GICC_BASE + 0x000CULL) -#define GICC_EOIR MAKE_REG32(GICC_BASE + 0x0010ull) - -#define GIC_PRI_HIGHEST_SECURE 0x00 -#define GIC_PRI_HIGHEST_NONSECURE 0x80 - -#define GIC_GROUP_SECURE 0 -#define GIC_GROUP_NONSECURE 1 - -/* To be called by FIQ handler. */ -void handle_registered_interrupt(void); - -/* Initializes the GIC. This must be called during wakeup. */ -void intr_initialize_gic(void); -void intr_initialize_gic_nonsecure(void); - -void intr_prepare_gicc_for_sleep(void); - - -void intr_register_handler(unsigned int id, void (*handler)(void)); -void intr_set_group(unsigned int id, int group); -void intr_set_pending(unsigned int id); -void intr_set_priority(unsigned int id, uint8_t priority); -void intr_set_cpu_mask(unsigned int id, uint8_t mask); -void intr_set_edge_level(unsigned int id, int edge_level); -void intr_set_enabled(unsigned int id, int enabled); -#endif diff --git a/exosphere/src/masterkey.c b/exosphere/src/masterkey.c deleted file mode 100644 index 86692dc69..000000000 --- a/exosphere/src/masterkey.c +++ /dev/null @@ -1,156 +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 <stdbool.h> -#include <stdint.h> -#include <string.h> - -#include "utils.h" -#include "configitem.h" -#include "masterkey.h" -#include "se.h" - -static unsigned int g_mkey_revision = 0; -static bool g_determined_mkey_revision = false; - -static uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10]; -static uint8_t g_old_devicekeys[MASTERKEY_NUM_NEW_DEVICE_KEYS - 1][0x10]; - - -/* TODO: Extend with new vectors, as needed. */ -/* Dev unit keys. */ -static const uint8_t mkey_vectors_dev[MASTERKEY_REVISION_MAX][0x10] = -{ - {0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE}, /* Zeroes encrypted with Master Key 00. */ - {0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23}, /* Master key 00 encrypted with Master key 01. */ - {0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D}, /* Master key 01 encrypted with Master key 02. */ - {0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */ - {0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */ - {0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */ - {0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */ - {0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */ - {0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */ - {0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */ - {0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */ -}; - -/* Retail unit keys. */ -static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = -{ - {0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */ - {0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */ - {0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */ - {0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */ - {0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */ - {0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */ - {0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */ - {0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */ - {0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */ - {0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */ - {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */ -}; - -bool check_mkey_revision(unsigned int revision, bool is_retail) { - uint8_t final_vector[0x10]; - - unsigned int check_keyslot = KEYSLOT_SWITCH_MASTERKEY; - if (revision > 0) { - /* Generate old master key array. */ - for (unsigned int i = revision; i > 0; i--) { - se_aes_ecb_decrypt_block(check_keyslot, g_old_masterkeys[i-1], 0x10, is_retail ? mkey_vectors[i] : mkey_vectors_dev[i], 0x10); - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[i-1], 0x10); - check_keyslot = KEYSLOT_SWITCH_TEMPKEY; - } - } - - se_aes_ecb_decrypt_block(check_keyslot, final_vector, 0x10, is_retail ? mkey_vectors[0] : mkey_vectors_dev[0], 0x10); - for (unsigned int i = 0; i < 0x10; i++) { - if (final_vector[i] != 0) { - return false; - } - } - return true; -} - -void mkey_detect_revision(void) { - if (g_determined_mkey_revision) { - generic_panic(); - } - - for (unsigned int rev = 0; rev < MASTERKEY_REVISION_MAX; rev++) { - if (check_mkey_revision(rev, configitem_is_retail())) { - g_determined_mkey_revision = true; - g_mkey_revision = rev; - break; - } - } - - /* We must have determined the master key, or we're not running on a Switch. */ - if (!g_determined_mkey_revision) { - /* Panic in bright red. */ - panic(0x00F00060); - } -} - -unsigned int mkey_get_revision(void) { - if (!g_determined_mkey_revision) { - generic_panic(); - } - - return g_mkey_revision; -} - -unsigned int mkey_get_keyslot(unsigned int revision) { - if (!g_determined_mkey_revision || revision >= MASTERKEY_REVISION_MAX) { - generic_panic(); - } - - if (revision > g_mkey_revision) { - generic_panic(); - } - - if (revision == g_mkey_revision) { - return KEYSLOT_SWITCH_MASTERKEY; - } else { - /* Load into a temp keyslot. */ - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[revision], 0x10); - return KEYSLOT_SWITCH_TEMPKEY; - } -} - -void set_old_devkey(unsigned int revision, const uint8_t *key) { - if (revision < MASTERKEY_REVISION_400_410 || MASTERKEY_REVISION_MAX <= revision) { - generic_panic(); - } - - memcpy(g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], key, 0x10); -} - -unsigned int devkey_get_keyslot(unsigned int revision) { - if (!g_determined_mkey_revision || revision > g_mkey_revision) { - generic_panic(); - } - - if (revision < MASTERKEY_REVISION_400_410) { - return KEYSLOT_SWITCH_4XOLDDEVICEKEY; - } else if (revision < g_mkey_revision) { - /* Load into a temp keyslot. */ - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], 0x10); - return KEYSLOT_SWITCH_TEMPKEY; - } else { - return KEYSLOT_SWITCH_DEVICEKEY; - } -} diff --git a/exosphere/src/masterkey.h b/exosphere/src/masterkey.h deleted file mode 100644 index 93a03c760..000000000 --- a/exosphere/src/masterkey.h +++ /dev/null @@ -1,49 +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_MASTERKEY_H -#define EXOSPHERE_MASTERKEY_H - -/* This is glue code to enable master key support across versions. */ - -/* TODO: Update to 0xC on release of new master key. */ -#define MASTERKEY_REVISION_MAX 0xB - -#define MASTERKEY_REVISION_100_230 0x00 -#define MASTERKEY_REVISION_300 0x01 -#define MASTERKEY_REVISION_301_302 0x02 -#define MASTERKEY_REVISION_400_410 0x03 -#define MASTERKEY_REVISION_500_510 0x04 -#define MASTERKEY_REVISION_600_610 0x05 -#define MASTERKEY_REVISION_620 0x06 -#define MASTERKEY_REVISION_700_800 0x07 -#define MASTERKEY_REVISION_810 0x08 -#define MASTERKEY_REVISION_900 0x09 -#define MASTERKEY_REVISION_910_CURRENT 0x0A - -#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410) - -/* This should be called early on in initialization. */ -void mkey_detect_revision(void); - -unsigned int mkey_get_revision(void); - -unsigned int mkey_get_keyslot(unsigned int revision); - -void set_old_devkey(unsigned int revision, const uint8_t *key); -unsigned int devkey_get_keyslot(unsigned int revision); - -#endif \ No newline at end of file diff --git a/exosphere/src/mc.c b/exosphere/src/mc.c deleted file mode 100644 index 41e999124..000000000 --- a/exosphere/src/mc.c +++ /dev/null @@ -1,162 +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 "memory_map.h" -#include "mc.h" -#include "exocfg.h" - -typedef struct { - uint64_t address; - uint64_t size; -} saved_carveout_info_t; - -static saved_carveout_info_t g_saved_carveouts[2] = { - {0x80060000ull, KERNEL_CARVEOUT_SIZE_MAX}, - {0x00000000ull, 0x00000000ull} -}; - -volatile security_carveout_t *get_carveout_by_id(unsigned int carveout) { - if (CARVEOUT_ID_MIN <= carveout && carveout <= CARVEOUT_ID_MAX) { - return (volatile security_carveout_t *)(MC_BASE + 0xC08ull + 0x50 * (carveout - CARVEOUT_ID_MIN)); - } - generic_panic(); - return NULL; -} - -void configure_gpu_ucode_carveout(void) { - /* Starting in 6.0.0, Carveout 2 is configured later on and adds read permission to TSEC. */ - /* This is a helper function to make this easier... */ - volatile security_carveout_t *carveout = get_carveout_by_id(2); - carveout->paddr_low = 0x80020000; - carveout->paddr_high = 0; - carveout->size_big_pages = 2; /* 0x40000 */ - carveout->client_access_0 = 0; - carveout->client_access_1 = 0; - carveout->client_access_2 = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) ? (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR) | BIT(CSR_TSECSRD)) : (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR)); - carveout->client_access_3 = 0; - carveout->client_access_4 = (BIT(CSR_GPUSRD2) | BIT(CSW_GPUSWR2)); - carveout->client_force_internal_access_0 = 0; - carveout->client_force_internal_access_1 = 0; - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - carveout->config = 0x440167E; -} - -void configure_default_carveouts(void) { - /* Configure Carveout 1 (UNUSED) */ - volatile security_carveout_t *carveout = get_carveout_by_id(1); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 0; - carveout->client_access_0 = 0; - carveout->client_access_1 = 0; - carveout->client_access_2 = 0; - carveout->client_access_3 = 0; - carveout->client_access_4 = 0; - carveout->client_force_internal_access_0 = 0; - carveout->client_force_internal_access_1 = 0; - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - carveout->config = 0x4000006; - - /* Configure Carveout 2 (GPU UCODE) */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - configure_gpu_ucode_carveout(); - } - - /* Configure Carveout 3 (UNUSED GPU) */ - carveout = get_carveout_by_id(3); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 0; - carveout->client_access_0 = 0; - carveout->client_access_1 = 0; - carveout->client_access_2 = (BIT(CSR_GPUSRD) | BIT(CSW_GPUSWR)); - carveout->client_access_3 = 0; - carveout->client_access_4 = (BIT(CSR_GPUSRD2) | BIT(CSW_GPUSWR2)); - carveout->client_force_internal_access_0 = 0; - carveout->client_force_internal_access_1 = 0; - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - carveout->config = 0x4401E7E; - - /* Configure default Kernel carveouts based on 2.0.0+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - /* Configure Carveout 4 (KERNEL_BUILTINS) */ - configure_kernel_carveout(4, g_saved_carveouts[0].address, g_saved_carveouts[0].size); - - /* Configure Carveout 5 (KERNEL_UNUSED) */ - configure_kernel_carveout(5, g_saved_carveouts[1].address, g_saved_carveouts[1].size); - } else { - for (unsigned int i = 4; i <= 5; i++) { - carveout = get_carveout_by_id(i); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 0; - carveout->client_access_0 = 0; - carveout->client_access_1 = 0; - carveout->client_access_2 = 0; - carveout->client_access_3 = 0; - carveout->client_access_4 = 0; - carveout->client_force_internal_access_0 = 0; - carveout->client_force_internal_access_1 = 0; - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - carveout->config = 0x4000006; - } - } -} - -void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size) { - if (carveout_id != 4 && carveout_id != 5) { - generic_panic(); - } - - g_saved_carveouts[carveout_id-4].address = address; - g_saved_carveouts[carveout_id-4].size = size; - - volatile security_carveout_t *carveout = get_carveout_by_id(carveout_id); - carveout->paddr_low = (uint32_t)(address & 0xFFFFFFFF); - carveout->paddr_high = (uint32_t)(address >> 32); - carveout->size_big_pages = (uint32_t)(size >> 17); - carveout->client_access_0 = (BIT(CSR_PTCR) | BIT(CSR_DISPLAY0A) | BIT(CSR_DISPLAY0AB) | BIT(CSR_DISPLAY0B) | BIT(CSR_DISPLAY0BB) | BIT(CSR_DISPLAY0C) | BIT(CSR_DISPLAY0CB) | BIT(CSR_AFIR) | BIT(CSR_DISPLAYHC) | BIT(CSR_DISPLAYHCB) | BIT(CSR_HDAR) | BIT(CSR_HOST1XDMAR) | BIT(CSR_HOST1XR) | BIT(CSR_NVENCSRD) | BIT(CSR_PPCSAHBDMAR) | BIT(CSR_PPCSAHBSLVR)); - carveout->client_access_1 = (BIT(CSR_MPCORER) | BIT(CSW_NVENCSWR) | BIT(CSW_AFIW) | BIT(CSW_HDAW) | BIT(CSW_HOST1XW) | BIT(CSW_MPCOREW) | BIT(CSW_PPCSAHBDMAW) | BIT(CSW_PPCSAHBSLVW)); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { - carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW)); - carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); - carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0) { - carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW)); - carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); - carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR) | BIT(CSR_TSECSRDB) | BIT(CSW_TSECSWRB)); - } else { - carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW) | BIT(CSR_TSECSRD) | BIT(CSW_TSECSWR)); - carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR)); - carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR)); - } - carveout->client_force_internal_access_0 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) && (carveout_id == 4)) ? BIT(CSR_AVPCARM7R) : 0; - carveout->client_force_internal_access_1 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) && (carveout_id == 4)) ? BIT(CSW_AVPCARM7W) : 0; - carveout->client_force_internal_access_2 = 0; - carveout->client_force_internal_access_3 = 0; - carveout->client_force_internal_access_4 = 0; - carveout->config = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0) ? 0x4CB : 0x8B; -} diff --git a/exosphere/src/mc.h b/exosphere/src/mc.h deleted file mode 100644 index 9d4c5f78d..000000000 --- a/exosphere/src/mc.h +++ /dev/null @@ -1,631 +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_MC_H -#define EXOSPHERE_MC_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 Memory Controller. */ - -static inline uintptr_t get_mc_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC); -} - -#define MC_BASE (get_mc_base()) -#define MAKE_MC_REG(n) MAKE_REG32(MC_BASE + n) - -#define MC_INTSTATUS 0x0 -#define MC_INTMASK 0x4 -#define MC_ERR_STATUS 0x8 -#define MC_ERR_ADR 0xc -#define MC_SMMU_CONFIG 0x10 -#define MC_SMMU_TLB_CONFIG 0x14 -#define MC_SMMU_PTC_CONFIG 0x18 -#define MC_SMMU_PTB_ASID 0x1c -#define MC_SMMU_PTB_DATA 0x20 -#define MC_SMMU_TLB_FLUSH 0x30 -#define MC_SMMU_PTC_FLUSH 0x34 -#define MC_SMMU_ASID_SECURITY 0x38 -#define MC_SMMU_ASID_SECURITY_1 0x3c -#define MC_SMMU_ASID_SECURITY_2 0x9e0 -#define MC_SMMU_ASID_SECURITY_3 0x9e4 -#define MC_SMMU_ASID_SECURITY_4 0x9e8 -#define MC_SMMU_ASID_SECURITY_5 0x9ec -#define MC_SMMU_ASID_SECURITY_6 0x9f0 -#define MC_SMMU_ASID_SECURITY_7 0x9f4 -#define MC_SMMU_AFI_ASID 0x238 -#define MC_SMMU_AVPC_ASID 0x23c -#define MC_SMMU_PPCS1_ASID 0x298 -#define MC_SMMU_TRANSLATION_ENABLE_0 0x228 -#define MC_SMMU_TRANSLATION_ENABLE_1 0x22c -#define MC_SMMU_TRANSLATION_ENABLE_2 0x230 -#define MC_SMMU_TRANSLATION_ENABLE_3 0x234 -#define MC_SMMU_TRANSLATION_ENABLE_4 0xb98 -#define MC_PCFIFO_CLIENT_CONFIG0 0xdd0 -#define MC_PCFIFO_CLIENT_CONFIG1 0xdd4 -#define MC_PCFIFO_CLIENT_CONFIG2 0xdd8 -#define MC_PCFIFO_CLIENT_CONFIG3 0xddc -#define MC_PCFIFO_CLIENT_CONFIG4 0xde0 -#define MC_EMEM_CFG 0x50 -#define MC_EMEM_ADR_CFG 0x54 -#define MC_EMEM_ADR_CFG_DEV0 0x58 -#define MC_EMEM_ADR_CFG_DEV1 0x5c -#define MC_EMEM_ADR_CFG_CHANNEL_MASK 0x60 -#define MC_EMEM_ADR_CFG_BANK_MASK_0 0x64 -#define MC_EMEM_ADR_CFG_BANK_MASK_1 0x68 -#define MC_EMEM_ADR_CFG_BANK_MASK_2 0x6c -#define MC_SECURITY_CFG0 0x70 -#define MC_SECURITY_CFG1 0x74 -#define MC_SECURITY_CFG3 0x9bc -#define MC_SECURITY_RSV 0x7c -#define MC_EMEM_ARB_CFG 0x90 -#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 -#define MC_EMEM_ARB_TIMING_RCD 0x98 -#define MC_EMEM_ARB_TIMING_RP 0x9c -#define MC_EMEM_ARB_TIMING_RC 0xa0 -#define MC_EMEM_ARB_TIMING_RAS 0xa4 -#define MC_EMEM_ARB_TIMING_FAW 0xa8 -#define MC_EMEM_ARB_TIMING_RRD 0xac -#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 -#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 -#define MC_EMEM_ARB_TIMING_R2R 0xb8 -#define MC_EMEM_ARB_TIMING_W2W 0xbc -#define MC_EMEM_ARB_TIMING_R2W 0xc0 -#define MC_EMEM_ARB_TIMING_W2R 0xc4 -#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0 -#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4 -#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0 -#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4 -#define MC_EMEM_ARB_DA_TURNS 0xd0 -#define MC_EMEM_ARB_DA_COVERS 0xd4 -#define MC_EMEM_ARB_MISC0 0xd8 -#define MC_EMEM_ARB_MISC1 0xdc -#define MC_EMEM_ARB_MISC2 0xc8 -#define MC_EMEM_ARB_RING1_THROTTLE 0xe0 -#define MC_EMEM_ARB_RING3_THROTTLE 0xe4 -#define MC_EMEM_ARB_NISO_THROTTLE 0x6b0 -#define MC_EMEM_ARB_OVERRIDE 0xe8 -#define MC_EMEM_ARB_RSV 0xec -#define MC_CLKEN_OVERRIDE 0xf4 -#define MC_TIMING_CONTROL_DBG 0xf8 -#define MC_TIMING_CONTROL 0xfc -#define MC_STAT_CONTROL 0x100 -#define MC_STAT_STATUS 0x104 -#define MC_STAT_EMC_CLOCK_LIMIT 0x108 -#define MC_STAT_EMC_CLOCK_LIMIT_MSBS 0x10c -#define MC_STAT_EMC_CLOCKS 0x110 -#define MC_STAT_EMC_CLOCKS_MSBS 0x114 -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO 0x118 -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO 0x158 -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI 0x11c -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI 0x15c -#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER 0xa20 -#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER 0xa24 -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_LO 0x198 -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_LO 0x1a8 -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_HI 0x19c -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_HI 0x1ac -#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_UPPER 0xa28 -#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_UPPER 0xa2c -#define MC_STAT_EMC_FILTER_SET0_ASID 0x1a0 -#define MC_STAT_EMC_FILTER_SET1_ASID 0x1b0 -#define MC_STAT_EMC_FILTER_SET0_SLACK_LIMIT 0x120 -#define MC_STAT_EMC_FILTER_SET1_SLACK_LIMIT 0x160 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 0x128 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 0x168 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 0x12c -#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 0x16c -#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 0x130 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 0x170 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 0x134 -#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 0xb88 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 0x174 -#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 0xb8c -#define MC_STAT_EMC_SET0_COUNT 0x138 -#define MC_STAT_EMC_SET0_COUNT_MSBS 0x13c -#define MC_STAT_EMC_SET1_COUNT 0x178 -#define MC_STAT_EMC_SET1_COUNT_MSBS 0x17c -#define MC_STAT_EMC_SET0_SLACK_ACCUM 0x140 -#define MC_STAT_EMC_SET0_SLACK_ACCUM_MSBS 0x144 -#define MC_STAT_EMC_SET1_SLACK_ACCUM 0x180 -#define MC_STAT_EMC_SET1_SLACK_ACCUM_MSBS 0x184 -#define MC_STAT_EMC_SET0_HISTO_COUNT 0x148 -#define MC_STAT_EMC_SET0_HISTO_COUNT_MSBS 0x14c -#define MC_STAT_EMC_SET1_HISTO_COUNT 0x188 -#define MC_STAT_EMC_SET1_HISTO_COUNT_MSBS 0x18c -#define MC_STAT_EMC_SET0_MINIMUM_SLACK_OBSERVED 0x150 -#define MC_STAT_EMC_SET1_MINIMUM_SLACK_OBSERVED 0x190 -#define MC_STAT_EMC_SET0_IDLE_CYCLE_COUNT 0x1b8 -#define MC_STAT_EMC_SET0_IDLE_CYCL_COUNT_MSBS 0x1bc -#define MC_STAT_EMC_SET1_IDLE_CYCLE_COUNT 0x1c8 -#define MC_STAT_EMC_SET1_IDLE_CYCL_COUNT_MSBS 0x1cc -#define MC_STAT_EMC_SET0_IDLE_CYCLE_PARTITION_SELECT 0x1c0 -#define MC_STAT_EMC_SET1_IDLE_CYCLE_PARTITION_SELECT 0x1d0 -#define MC_CLIENT_HOTRESET_CTRL 0x200 -#define MC_CLIENT_HOTRESET_CTRL_1 0x970 -#define MC_CLIENT_HOTRESET_STATUS 0x204 -#define MC_CLIENT_HOTRESET_STATUS_1 0x974 -#define MC_EMEM_ARB_ISOCHRONOUS_0 0x208 -#define MC_EMEM_ARB_ISOCHRONOUS_1 0x20c -#define MC_EMEM_ARB_ISOCHRONOUS_2 0x210 -#define MC_EMEM_ARB_ISOCHRONOUS_3 0x214 -#define MC_EMEM_ARB_ISOCHRONOUS_4 0xb94 -#define MC_EMEM_ARB_HYSTERESIS_0 0x218 -#define MC_EMEM_ARB_HYSTERESIS_1 0x21c -#define MC_EMEM_ARB_HYSTERESIS_2 0x220 -#define MC_EMEM_ARB_HYSTERESIS_3 0x224 -#define MC_EMEM_ARB_HYSTERESIS_4 0xb84 -#define MC_EMEM_ARB_DHYSTERESIS_0 0xbb0 -#define MC_EMEM_ARB_DHYSTERESIS_1 0xbb4 -#define MC_EMEM_ARB_DHYSTERESIS_2 0xbb8 -#define MC_EMEM_ARB_DHYSTERESIS_3 0xbbc -#define MC_EMEM_ARB_DHYSTERESIS_4 0xbc0 -#define MC_EMEM_ARB_DHYST_CTRL 0xbcc -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8 -#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec -#define MC_RESERVED_RSV 0x3fc -#define MC_DISB_EXTRA_SNAP_LEVELS 0x408 -#define MC_APB_EXTRA_SNAP_LEVELS 0x2a4 -#define MC_AHB_EXTRA_SNAP_LEVELS 0x2a0 -#define MC_USBD_EXTRA_SNAP_LEVELS 0xa18 -#define MC_ISP_EXTRA_SNAP_LEVELS 0xa08 -#define MC_AUD_EXTRA_SNAP_LEVELS 0xa10 -#define MC_MSE_EXTRA_SNAP_LEVELS 0x40c -#define MC_GK2_EXTRA_SNAP_LEVELS 0xa40 -#define MC_A9AVPPC_EXTRA_SNAP_LEVELS 0x414 -#define MC_FTOP_EXTRA_SNAP_LEVELS 0x2bc -#define MC_JPG_EXTRA_SNAP_LEVELS 0xa3c -#define MC_HOST_EXTRA_SNAP_LEVELS 0xa14 -#define MC_SAX_EXTRA_SNAP_LEVELS 0x2c0 -#define MC_DIS_EXTRA_SNAP_LEVELS 0x2ac -#define MC_VICPC_EXTRA_SNAP_LEVELS 0xa1c -#define MC_HDAPC_EXTRA_SNAP_LEVELS 0xa48 -#define MC_AVP_EXTRA_SNAP_LEVELS 0x2a8 -#define MC_USBX_EXTRA_SNAP_LEVELS 0x404 -#define MC_PCX_EXTRA_SNAP_LEVELS 0x2b8 -#define MC_SD_EXTRA_SNAP_LEVELS 0xa04 -#define MC_DFD_EXTRA_SNAP_LEVELS 0xa4c -#define MC_VE_EXTRA_SNAP_LEVELS 0x2d8 -#define MC_GK_EXTRA_SNAP_LEVELS 0xa00 -#define MC_VE2_EXTRA_SNAP_LEVELS 0x410 -#define MC_SDM_EXTRA_SNAP_LEVELS 0xa44 -#define MC_VIDEO_PROTECT_BOM 0x648 -#define MC_VIDEO_PROTECT_SIZE_MB 0x64c -#define MC_VIDEO_PROTECT_BOM_ADR_HI 0x978 -#define MC_VIDEO_PROTECT_REG_CTRL 0x650 -#define MC_ERR_VPR_STATUS 0x654 -#define MC_ERR_VPR_ADR 0x658 -#define MC_VIDEO_PROTECT_VPR_OVERRIDE 0x418 -#define MC_VIDEO_PROTECT_VPR_OVERRIDE1 0x590 -#define MC_IRAM_BOM 0x65c -#define MC_IRAM_TOM 0x660 -#define MC_IRAM_ADR_HI 0x980 -#define MC_IRAM_REG_CTRL 0x964 -#define MC_EMEM_CFG_ACCESS_CTRL 0x664 -#define MC_TZ_SECURITY_CTRL 0x668 -#define MC_EMEM_ARB_OUTSTANDING_REQ_RING3 0x66c -#define MC_EMEM_ARB_OUTSTANDING_REQ_NISO 0x6b4 -#define MC_EMEM_ARB_RING0_THROTTLE_MASK 0x6bc -#define MC_EMEM_ARB_NISO_THROTTLE_MASK 0x6b8 -#define MC_EMEM_ARB_NISO_THROTTLE_MASK_1 0xb80 -#define MC_SEC_CARVEOUT_BOM 0x670 -#define MC_SEC_CARVEOUT_SIZE_MB 0x674 -#define MC_SEC_CARVEOUT_ADR_HI 0x9d4 -#define MC_SEC_CARVEOUT_REG_CTRL 0x678 -#define MC_ERR_SEC_STATUS 0x67c -#define MC_ERR_SEC_ADR 0x680 -#define MC_PC_IDLE_CLOCK_GATE_CONFIG 0x684 -#define MC_STUTTER_CONTROL 0x688 -#define MC_RESERVED_RSV_1 0x958 -#define MC_DVFS_PIPE_SELECT 0x95c -#define MC_AHB_PTSA_MIN 0x4e0 -#define MC_AUD_PTSA_MIN 0x54c -#define MC_MLL_MPCORER_PTSA_RATE 0x44c -#define MC_RING2_PTSA_RATE 0x440 -#define MC_USBD_PTSA_RATE 0x530 -#define MC_USBX_PTSA_MIN 0x528 -#define MC_USBD_PTSA_MIN 0x534 -#define MC_APB_PTSA_MAX 0x4f0 -#define MC_JPG_PTSA_RATE 0x584 -#define MC_DIS_PTSA_MIN 0x420 -#define MC_AVP_PTSA_MAX 0x4fc -#define MC_AVP_PTSA_RATE 0x4f4 -#define MC_RING1_PTSA_MIN 0x480 -#define MC_DIS_PTSA_MAX 0x424 -#define MC_SD_PTSA_MAX 0x4d8 -#define MC_MSE_PTSA_RATE 0x4c4 -#define MC_VICPC_PTSA_MIN 0x558 -#define MC_PCX_PTSA_MAX 0x4b4 -#define MC_ISP_PTSA_RATE 0x4a0 -#define MC_A9AVPPC_PTSA_MIN 0x48c -#define MC_RING2_PTSA_MAX 0x448 -#define MC_AUD_PTSA_RATE 0x548 -#define MC_HOST_PTSA_MIN 0x51c -#define MC_MLL_MPCORER_PTSA_MAX 0x454 -#define MC_SD_PTSA_MIN 0x4d4 -#define MC_RING1_PTSA_RATE 0x47c -#define MC_JPG_PTSA_MIN 0x588 -#define MC_HDAPC_PTSA_MIN 0x62c -#define MC_AVP_PTSA_MIN 0x4f8 -#define MC_JPG_PTSA_MAX 0x58c -#define MC_VE_PTSA_MAX 0x43c -#define MC_DFD_PTSA_MAX 0x63c -#define MC_VICPC_PTSA_RATE 0x554 -#define MC_GK_PTSA_MAX 0x544 -#define MC_VICPC_PTSA_MAX 0x55c -#define MC_SDM_PTSA_MAX 0x624 -#define MC_SAX_PTSA_RATE 0x4b8 -#define MC_PCX_PTSA_MIN 0x4b0 -#define MC_APB_PTSA_MIN 0x4ec -#define MC_GK2_PTSA_MIN 0x614 -#define MC_PCX_PTSA_RATE 0x4ac -#define MC_RING1_PTSA_MAX 0x484 -#define MC_HDAPC_PTSA_RATE 0x628 -#define MC_MLL_MPCORER_PTSA_MIN 0x450 -#define MC_GK2_PTSA_MAX 0x618 -#define MC_AUD_PTSA_MAX 0x550 -#define MC_GK2_PTSA_RATE 0x610 -#define MC_ISP_PTSA_MAX 0x4a8 -#define MC_DISB_PTSA_RATE 0x428 -#define MC_VE2_PTSA_MAX 0x49c -#define MC_DFD_PTSA_MIN 0x638 -#define MC_FTOP_PTSA_RATE 0x50c -#define MC_A9AVPPC_PTSA_RATE 0x488 -#define MC_VE2_PTSA_MIN 0x498 -#define MC_USBX_PTSA_MAX 0x52c -#define MC_DIS_PTSA_RATE 0x41c -#define MC_USBD_PTSA_MAX 0x538 -#define MC_A9AVPPC_PTSA_MAX 0x490 -#define MC_USBX_PTSA_RATE 0x524 -#define MC_FTOP_PTSA_MAX 0x514 -#define MC_HDAPC_PTSA_MAX 0x630 -#define MC_SD_PTSA_RATE 0x4d0 -#define MC_DFD_PTSA_RATE 0x634 -#define MC_FTOP_PTSA_MIN 0x510 -#define MC_SDM_PTSA_RATE 0x61c -#define MC_AHB_PTSA_RATE 0x4dc -#define MC_SMMU_SMMU_PTSA_MAX 0x460 -#define MC_RING2_PTSA_MIN 0x444 -#define MC_SDM_PTSA_MIN 0x620 -#define MC_APB_PTSA_RATE 0x4e8 -#define MC_MSE_PTSA_MIN 0x4c8 -#define MC_HOST_PTSA_RATE 0x518 -#define MC_VE_PTSA_RATE 0x434 -#define MC_AHB_PTSA_MAX 0x4e4 -#define MC_SAX_PTSA_MIN 0x4bc -#define MC_SMMU_SMMU_PTSA_MIN 0x45c -#define MC_ISP_PTSA_MIN 0x4a4 -#define MC_HOST_PTSA_MAX 0x520 -#define MC_SAX_PTSA_MAX 0x4c0 -#define MC_VE_PTSA_MIN 0x438 -#define MC_GK_PTSA_MIN 0x540 -#define MC_MSE_PTSA_MAX 0x4cc -#define MC_DISB_PTSA_MAX 0x430 -#define MC_DISB_PTSA_MIN 0x42c -#define MC_SMMU_SMMU_PTSA_RATE 0x458 -#define MC_VE2_PTSA_RATE 0x494 -#define MC_GK_PTSA_RATE 0x53c -#define MC_PTSA_GRANT_DECREMENT 0x960 -#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4 -#define MC_LATENCY_ALLOWANCE_AXIAP_0 0x3a0 -#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380 -#define MC_LATENCY_ALLOWANCE_ISP2B_0 0x384 -#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc -#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8 -#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370 -#define MC_LATENCY_ALLOWANCE_SE_0 0x3e0 -#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374 -#define MC_LATENCY_ALLOWANCE_DC_0 0x2e8 -#define MC_LATENCY_ALLOWANCE_VIC_0 0x394 -#define MC_LATENCY_ALLOWANCE_DCB_1 0x2f8 -#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8 -#define MC_LATENCY_ALLOWANCE_DCB_2 0x2fc -#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390 -#define MC_LATENCY_ALLOWANCE_DC_2 0x2f0 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB 0x694 -#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348 -#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c -#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344 -#define MC_LATENCY_ALLOWANCE_TSECB_0 0x3f0 -#define MC_LATENCY_ALLOWANCE_AFI_0 0x2e0 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B 0x698 -#define MC_LATENCY_ALLOWANCE_DC_1 0x2ec -#define MC_LATENCY_ALLOWANCE_APE_0 0x3dc -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C 0x6a0 -#define MC_LATENCY_ALLOWANCE_A9AVP_0 0x3a4 -#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8 -#define MC_LATENCY_ALLOWANCE_DCB_0 0x2f4 -#define MC_LATENCY_ALLOWANCE_HC_1 0x314 -#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0 -#define MC_LATENCY_ALLOWANCE_NVJPG_0 0x3e4 -#define MC_LATENCY_ALLOWANCE_PTC_0 0x34c -#define MC_LATENCY_ALLOWANCE_ETR_0 0x3ec -#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320 -#define MC_LATENCY_ALLOWANCE_VI2_0 0x398 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB 0x69c -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB 0x6a4 -#define MC_LATENCY_ALLOWANCE_SATA_0 0x350 -#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A 0x690 -#define MC_LATENCY_ALLOWANCE_HC_0 0x310 -#define MC_LATENCY_ALLOWANCE_DC_3 0x3c8 -#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac -#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4 -#define MC_LATENCY_ALLOWANCE_ISP2B_1 0x388 -#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328 -#define MC_LATENCY_ALLOWANCE_HDA_0 0x318 -#define MC_MIN_LENGTH_APE_0 0xb34 -#define MC_MIN_LENGTH_DCB_2 0x8a8 -#define MC_MIN_LENGTH_A9AVP_0 0x950 -#define MC_MIN_LENGTH_TSEC_0 0x93c -#define MC_MIN_LENGTH_DC_1 0x898 -#define MC_MIN_LENGTH_AXIAP_0 0x94c -#define MC_MIN_LENGTH_ISP2B_0 0x930 -#define MC_MIN_LENGTH_VI2_0 0x944 -#define MC_MIN_LENGTH_DCB_0 0x8a0 -#define MC_MIN_LENGTH_DCB_1 0x8a4 -#define MC_MIN_LENGTH_PPCS_1 0x8f4 -#define MC_MIN_LENGTH_NVJPG_0 0xb3c -#define MC_MIN_LENGTH_HDA_0 0x8c4 -#define MC_MIN_LENGTH_NVENC_0 0x8d4 -#define MC_MIN_LENGTH_SDMMC_0 0xb18 -#define MC_MIN_LENGTH_ISP2B_1 0x934 -#define MC_MIN_LENGTH_HC_1 0x8c0 -#define MC_MIN_LENGTH_DC_3 0xb20 -#define MC_MIN_LENGTH_AVPC_0 0x890 -#define MC_MIN_LENGTH_VIC_0 0x940 -#define MC_MIN_LENGTH_ISP2_0 0x91c -#define MC_MIN_LENGTH_HC_0 0x8bc -#define MC_MIN_LENGTH_SE_0 0xb38 -#define MC_MIN_LENGTH_NVDEC_0 0xb30 -#define MC_MIN_LENGTH_SATA_0 0x8fc -#define MC_MIN_LENGTH_DC_0 0x894 -#define MC_MIN_LENGTH_XUSB_1 0x92c -#define MC_MIN_LENGTH_DC_2 0x89c -#define MC_MIN_LENGTH_SDMMCAA_0 0xb14 -#define MC_MIN_LENGTH_GPU_0 0xb04 -#define MC_MIN_LENGTH_ETR_0 0xb44 -#define MC_MIN_LENGTH_AFI_0 0x88c -#define MC_MIN_LENGTH_PPCS_0 0x8f0 -#define MC_MIN_LENGTH_ISP2_1 0x920 -#define MC_MIN_LENGTH_XUSB_0 0x928 -#define MC_MIN_LENGTH_MPCORE_0 0x8cc -#define MC_MIN_LENGTH_TSECB_0 0xb48 -#define MC_MIN_LENGTH_SDMMCA_0 0xb10 -#define MC_MIN_LENGTH_GPU2_0 0xb40 -#define MC_MIN_LENGTH_SDMMCAB_0 0xb1c -#define MC_MIN_LENGTH_PTC_0 0x8f8 -#define MC_EMEM_ARB_OVERRIDE_1 0x968 -#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 0x984 -#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 0x988 -#define MC_EMEM_ARB_STATS_0 0x990 -#define MC_EMEM_ARB_STATS_1 0x994 -#define MC_MTS_CARVEOUT_BOM 0x9a0 -#define MC_MTS_CARVEOUT_SIZE_MB 0x9a4 -#define MC_MTS_CARVEOUT_ADR_HI 0x9a8 -#define MC_MTS_CARVEOUT_REG_CTRL 0x9ac -#define MC_ERR_MTS_STATUS 0x9b0 -#define MC_ERR_MTS_ADR 0x9b4 -#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 -#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 0xd74 -#define MC_SECURITY_CARVEOUT4_CFG0 0xcf8 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 0xd10 -#define MC_SECURITY_CARVEOUT4_SIZE_128KB 0xd04 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 0xc28 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 0xc30 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 0xc8c -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 0xd1c -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 0xd70 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 0xc2c -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 0xd7c -#define MC_SECURITY_CARVEOUT3_SIZE_128KB 0xcb4 -#define MC_SECURITY_CARVEOUT2_CFG0 0xc58 -#define MC_SECURITY_CARVEOUT1_CFG0 0xc08 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 0xc84 -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 0xc68 -#define MC_SECURITY_CARVEOUT3_BOM 0xcac -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 0xc70 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 0xd78 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 0xc7c -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 0xd18 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 0xcbc -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 0xc38 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 0xc34 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 0xcc0 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 0xd60 -#define MC_SECURITY_CARVEOUT3_CFG0 0xca8 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 0xcb8 -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 0xc88 -#define MC_SECURITY_CARVEOUT2_SIZE_128KB 0xc64 -#define MC_SECURITY_CARVEOUT5_BOM_HI 0xd50 -#define MC_SECURITY_CARVEOUT1_SIZE_128KB 0xc14 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 0xd14 -#define MC_SECURITY_CARVEOUT1_BOM 0xc0c -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 0xd2c -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 0xd68 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 0xcc8 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 0xd58 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 0xd24 -#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 0xcc4 -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 0xc78 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 0xc1c -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 0xc18 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 0xd28 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 0xd5c -#define MC_SECURITY_CARVEOUT3_BOM_HI 0xcb0 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 0xcd8 -#define MC_SECURITY_CARVEOUT2_BOM_HI 0xc60 -#define MC_SECURITY_CARVEOUT4_BOM_HI 0xd00 -#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 0xd64 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 0xcdc -#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 0xc80 -#define MC_SECURITY_CARVEOUT5_SIZE_128KB 0xd54 -#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 0xd20 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 0xcd4 -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 0xd0c -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 0xc74 -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 0xccc -#define MC_SECURITY_CARVEOUT4_BOM 0xcfc -#define MC_SECURITY_CARVEOUT5_CFG0 0xd48 -#define MC_SECURITY_CARVEOUT2_BOM 0xc5c -#define MC_SECURITY_CARVEOUT5_BOM 0xd4c -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 0xc24 -#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 0xd6c -#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 0xcd0 -#define MC_SECURITY_CARVEOUT1_BOM_HI 0xc10 -#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 0xc20 -#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 0xc3c -#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 0xc6c -#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 0xd08 -#define MC_ERR_APB_ASID_UPDATE_STATUS 0x9d0 -#define MC_DA_CONFIG0 0x9dc - -/* Virtual aliases */ -#define VIRT_MC_SECURITY_CFG3 MAKE_MC_REG(MC_SECURITY_CFG3) - -/* Memory Controller clients */ -#define CLIENT_ACCESS_NUM_CLIENTS 32 -typedef enum { - /* _ACCESS0 */ - CSR_PTCR = (0 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0A = (1 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0AB = (2 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0B = (3 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0BB = (4 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0C = (5 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAY0CB = (6 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_AFIR = (14 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_AVPCARM7R = (15 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAYHC = (16 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_DISPLAYHCB = (17 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HDAR = (21 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HOST1XDMAR = (22 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_HOST1XR = (23 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_NVENCSRD = (28 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_PPCSAHBDMAR = (29 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_PPCSAHBSLVR = (30 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - CSR_SATAR = (31 - (CLIENT_ACCESS_NUM_CLIENTS * 0)), - - /* _ACCESS1 */ - CSR_VDEBSEVR = (34 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDEMBER = (35 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDEMCER = (36 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_VDETPER = (37 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_MPCORELPR = (38 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSR_MPCORER = (39 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_NVENCSWR = (43 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_AFIW = (49 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_AVPCARM7W = (50 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_HDAW = (53 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_HOST1XW = (54 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_MPCORELPW = (56 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_MPCOREW = (57 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_PPCSAHBDMAW = (59 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_PPCSAHBSLVW = (60 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_SATAW = (61 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_VDEBSEVW = (62 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - CSW_VDEDBGW = (63 - (CLIENT_ACCESS_NUM_CLIENTS * 1)), - - /* _ACCESS2 */ - CSW_VDEMBEW = (64 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_VDETPMW = (65 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_ISPRA = (68 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWA = (70 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWB = (71 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_XUSB_HOSTR = (74 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_XUSB_HOSTW = (75 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_XUSB_DEVR = (76 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_XUSB_DEVW = (77 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_ISPRAB = (78 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWAB = (80 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_ISPWBB = (81 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_TSECSRD = (84 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_TSECSWR = (85 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_A9AVPSCR = (86 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_A9AVPSCW = (87 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_GPUSRD = (88 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSW_GPUSWR = (89 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - CSR_DISPLAYT = (90 - (CLIENT_ACCESS_NUM_CLIENTS * 2)), - - /* _ACCESS3 */ - CSR_SDMMCRA = (96 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCRAA = (97 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCR = (98 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_SDMMCRAB = (99 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWA = (100 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWAA = (101 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCW = (102 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_SDMMCWAB = (103 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_VICSRD = (108 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_VICSWR = (109 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_VIW = (114 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_DISPLAYD = (115 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_NVDECSRD = (120 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_NVDECSWR = (121 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_APER = (122 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_APEW = (123 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSR_NVJPGSRD = (126 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - CSW_NVJPGSWR = (127 - (CLIENT_ACCESS_NUM_CLIENTS * 3)), - - /* _ACCESS4 */ - CSR_SESRD = (128 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_SESWR = (129 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_AXIAPR = (130 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_AXIAPW = (131 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_ETRR = (132 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_ETRW = (133 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_TSECSRDB = (134 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_TSECSWRB = (135 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSR_GPUSRD2 = (136 - (CLIENT_ACCESS_NUM_CLIENTS * 4)), - CSW_GPUSWR2 = (137 - (CLIENT_ACCESS_NUM_CLIENTS * 4)) -} McClient; - -/* Memory Controller carveouts */ -#define CARVEOUT_ID_MIN 1 -#define CARVEOUT_ID_MAX 5 -#define KERNEL_CARVEOUT_SIZE_MAX 0x1FFE0000 -typedef struct { - uint32_t config; - uint32_t paddr_low; - uint32_t paddr_high; - uint32_t size_big_pages; - uint32_t client_access_0; - uint32_t client_access_1; - uint32_t client_access_2; - uint32_t client_access_3; - uint32_t client_access_4; - uint32_t client_force_internal_access_0; - uint32_t client_force_internal_access_1; - uint32_t client_force_internal_access_2; - uint32_t client_force_internal_access_3; - uint32_t client_force_internal_access_4; - uint8_t padding[0x18]; -} security_carveout_t; - -volatile security_carveout_t *get_carveout_by_id(unsigned int carveout); -void configure_default_carveouts(void); -void configure_gpu_ucode_carveout(void); -void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size); - -#endif \ No newline at end of file diff --git a/exosphere/src/mc0.h b/exosphere/src/mc0.h deleted file mode 100644 index 8e4f51848..000000000 --- a/exosphere/src/mc0.h +++ /dev/null @@ -1,32 +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_MC0_H -#define EXOSPHERE_MC0_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 MC0. */ - -static inline uintptr_t get_mc0_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC0); -} - -#define MC0_BASE (get_mc0_base()) -#define MAKE_MC0_REG(n) MAKE_REG32(MC0_BASE + n) - -#endif diff --git a/exosphere/src/mc1.h b/exosphere/src/mc1.h deleted file mode 100644 index bcdfe03be..000000000 --- a/exosphere/src/mc1.h +++ /dev/null @@ -1,32 +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_MC0_H -#define EXOSPHERE_MC0_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 MC1. */ - -static inline uintptr_t get_mc1_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC1); -} - -#define MC1_BASE (get_mc1_base()) -#define MAKE_MC1_REG(n) MAKE_REG32(MC1_BASE + n) - -#endif diff --git a/exosphere/src/memory_map.h b/exosphere/src/memory_map.h deleted file mode 100644 index aca65bf69..000000000 --- a/exosphere/src/memory_map.h +++ /dev/null @@ -1,234 +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_MEMORY_MAP_H -#define EXOSPHERE_MEMORY_MAP_H - -#include "mmu.h" -#include "preprocessor.h" - -#define ATTRIB_MEMTYPE_NORMAL MMU_PTE_BLOCK_MEMTYPE(MMU_MT_NORMAL) -#define ATTRIB_MEMTYPE_DEVICE MMU_PTE_BLOCK_MEMTYPE(MMU_MT_DEVICE_NGNRE) - -/* Identity mappings (addr, size, additional attributes, is block range) */ -#define _MMAPID0 ( 0x40006000ull, 0x01000ull, 0ull, false ) /* Part of iRAM-A, where our coldboot crt0 is */ -#define _MMAPID1 ( 0x40020000ull, 0x20000ull, 0ull, false ) /* iRAM-C+D */ -#define _MMAPID2 ( 0x7C010000ull, 0x10000ull, 0ull, false ) /* TZRAM (contains the secmon's warmboot crt0) */ -#define _MMAPID3 ( 0x80000000ull, 4ull << 30, MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_NS, true ) /* DRAM (4GB) */ - -/* MMIO (addr, size, is secure) */ -#define _MMAPDEV0 ( 0x50041000ull, 0x1000ull, true ) /* ARM Interrupt Distributor */ -#define _MMAPDEV1 ( 0x50042000ull, 0x2000ull, true ) /* Interrupt Controller Physical CPU interface */ -#define _MMAPDEV2 ( 0x70006000ull, 0x1000ull, false ) /* UART */ -#define _MMAPDEV3 ( 0x60006000ull, 0x1000ull, false ) /* Clock and Reset */ -#define _MMAPDEV4 ( 0x7000E000ull, 0x1000ull, true ) /* RTC, PMC */ -#define _MMAPDEV5 ( 0x60005000ull, 0x1000ull, true ) /* TMRs, WDTs */ -#define _MMAPDEV6 ( 0x6000C000ull, 0x1000ull, true ) /* System Registers */ -#define _MMAPDEV7 ( 0x70012000ull, 0x2000ull, true ) /* SE */ -#define _MMAPDEV8 ( 0x700F0000ull, 0x1000ull, true ) /* SYSCTR0 */ -#define _MMAPDEV9 ( 0x70019000ull, 0x1000ull, true ) /* MC */ -#define _MMAPDEV10 ( 0x7000F000ull, 0x1000ull, true ) /* FUSE (0x7000F800) */ -#define _MMAPDEV11 ( 0x70000000ull, 0x4000ull, true ) /* MISC */ -#define _MMAPDEV12 ( 0x60007000ull, 0x1000ull, true ) /* Flow Controller */ -#define _MMAPDEV13 ( 0x40002000ull, 0x1000ull, true ) /* NX bootloader mailbox page */ -#define _MMAPDEV14 ( 0x7000D000ull, 0x1000ull, true ) /* I2C-5,6 - SPI 2B-1 to 4 */ -#define _MMAPDEV15 ( 0x6000D000ull, 0x1000ull, true ) /* GPIO-1 - GPIO-8 */ -#define _MMAPDEV16 ( 0x7000C000ull, 0x1000ull, true ) /* I2C-I2C4 */ -#define _MMAPDEV17 ( 0x6000F000ull, 0x1000ull, true ) /* Exception vectors */ -#define _MMAPDEV18 ( 0x7001C000ull, 0x1000ull, true ) /* MC0 */ -#define _MMAPDEV19 ( 0x7001D000ull, 0x1000ull, true ) /* MC1 */ -#define _MMAPDEV20 ( 0x00000000ull, 0x1000ull, true ) /* AMS irampage, NOT mapped at startup */ -#define _MMAPDEV21 ( 0x00000000ull, 0x1000ull, true ) /* AMS userpage, NOT mapped at startup */ -#define _MMAPDEV22 ( 0x40038000ull, 0x1000ull, true ) /* DEBUG: IRAM */ - -/* MMIO 7.0.0+. (addr). */ -#define _MMAPDEV7X0 ( 0x50041000ull ) /* ARM Interrupt Distributor */ -#define _MMAPDEV7X1 ( 0x50042000ull ) /* Interrupt Controller Physical CPU interface */ -#define _MMAPDEV7X2 ( 0x70006000ull ) /* UART */ -#define _MMAPDEV7X3 ( 0x60006000ull ) /* Clock and Reset */ -#define _MMAPDEV7X4 ( 0x7000E000ull ) /* RTC, PMC */ -#define _MMAPDEV7X5 ( 0x60005000ull ) /* TMRs, WDTs */ -#define _MMAPDEV7X6 ( 0x6000C000ull ) /* System Registers */ -#define _MMAPDEV7X7 ( 0x70012000ull ) /* SE */ -#define _MMAPDEV7X8 ( 0x700F0000ull ) /* SYSCTR0 */ -#define _MMAPDEV7X9 ( 0x70019000ull ) /* MC */ -#define _MMAPDEV7X10 ( 0x7000F000ull ) /* FUSE (0x7000F800) */ -#define _MMAPDEV7X11 ( 0x70000000ull ) /* MISC */ -#define _MMAPDEV7X12 ( 0x60007000ull ) /* Flow Controller */ -#define _MMAPDEV7X13 ( 0x40000000ull ) /* NX bootloader mailbox page */ -#define _MMAPDEV7X14 ( 0x7000D000ull ) /* I2C-5,6 - SPI 2B-1 to 4 */ -#define _MMAPDEV7X15 ( 0x6000D000ull ) /* GPIO-1 - GPIO-8 */ -#define _MMAPDEV7X16 ( 0x7000C000ull ) /* I2C-I2C4 */ -#define _MMAPDEV7X17 ( 0x6000F000ull ) /* Exception vectors */ -#define _MMAPDEV7X18 ( 0x7001C000ull ) /* MC0 */ -#define _MMAPDEV7X19 ( 0x7001D000ull ) /* MC1 */ -#define _MMAPDEV7X20 ( 0x00000000ull ) /* AMS irampage, NOT mapped at startup */ -#define _MMAPDEV7X21 ( 0x00000000ull ) /* AMS userpage, NOT mapped at startup */ -#define _MMAPDEV7X22 ( 0x40038000ull ) /* DEBUG: IRAM */ - -/* LP0 entry ram segments (addr, size, additional attributes) */ -#define _MMAPLP0ES0 ( 0x40020000ull, 0x10000ull, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE ) /* Encrypted TZRAM */ -#define _MMAPLP0ES1 ( 0x40003000ull, 0x01000ull, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE ) /* LP0 entry code */ -#define _MMAPLP0ES2 ( 0x7C010000ull, 0x10000ull, MMU_AP_PRIV_RO | ATTRIB_MEMTYPE_NORMAL ) /* TZRAM to encrypt */ - -/* Warmboot data ram segments (addr, size, additional attributes) */ -#define _MMAPWBS0 ( 0x8000F000ull, 0x01000ull, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE ) /* Encrypted SE state for bootROM */ -#define _MMAPWBS1 ( 0x80010000ull, 0x10000ull, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE ) /* Encrypted TZRAM for warmboot.bin */ - -/* TZRAM segments (offset, size, VA increment, is executable) */ -#define _MMAPTZS0 ( 0x3000ull, 0x10000 - 0x2000 - 0x3000ull, 0x10000ull, true ) /* Warmboot crt0 sections and main code segment */ -#define _MMAPTZS1 ( 0x10000 - 0x2000ull, 0x2000ull, 0x04000ull, true ) /* pk2ldr segment */ -#define _MMAPTZS2 ( 0ull, 0ull, 0x02000ull, false ) /* SPL .bss buffer, NOT mapped at startup */ -#define _MMAPTZS3 ( 0x10000 - 0x2000ull, 0x1000ull, 0x02000ull, false ) /* Core 0ull1,2 stack */ -#define _MMAPTZS4 ( 0x10000 - 0x1000ull, 0x1000ull, 0x02000ull, false ) /* Core 3 stack */ -#define _MMAPTZS5 ( 0ull, 0x1000ull, 0x02000ull, true ) /* Secure Monitor exception vectors, some init stacks */ -#define _MMAPTZS6 ( 0x1000ull, 0x1000ull, 0x02000ull, false ) /* L2 translation table */ -#define _MMAPTZS7 ( 0x2000ull, 0x1000ull, 0x02000ull, false ) /* L3 translation table */ - -/* TZRAM segments for 5.0.0+. (offset). */ -#define _MMAPTZ5XS0 ( 0x3000ull ) /* Warmboot crt0 sections and main code segment */ -#define _MMAPTZ5XS1 ( 0ull ) /* pk2ldr segment */ -#define _MMAPTZ5XS2 ( 0ull ) /* SPL .bss buffer, NOT mapped at startup */ -#define _MMAPTZ5XS3 ( 0ull ) /* Core 0ull1,2 stack */ -#define _MMAPTZ5XS4 ( 0x1000ull ) /* Core 3 stack */ -#define _MMAPTZ5XS5 ( 0x2000ull ) /* Secure Monitor exception vectors, some init stacks */ -#define _MMAPTZ5XS6 ( 0x10000 - 0x2000ull ) /* L2 translation table */ -#define _MMAPTZ5XS7 ( 0x10000 - 0x1000ull ) /* L3 translation table */ - -#define MMIO_BASE 0x1F0080000ull -#define LP0_ENTRY_RAM_SEGMENT_BASE (MMIO_BASE + 0x000100000ull) -#define WARMBOOT_RAM_SEGMENT_BASE (LP0_ENTRY_RAM_SEGMENT_BASE + 0x000047000ull) /* increment seems to be arbitrary ? */ -#define TZRAM_SEGMENT_BASE (MMIO_BASE + 0x000160000ull) - -#define IDENTITY_MAPPING_IRAM_A_CCRT0 0 -#define IDENTITY_MAPPING_IRAM_CD 1 -#define IDENTITY_MAPPING_TZRAM 2 -#define IDENTITY_MAPPING_DRAM 3 -#define IDENTIY_MAPPING_ID_MAX 4 - -#define MMIO_DEVID_GICD 0 -#define MMIO_DEVID_GICC 1 -#define MMIO_DEVID_UART 2 -#define MMIO_DEVID_CLKRST 3 -#define MMIO_DEVID_RTC_PMC 4 -#define MMIO_DEVID_TMRs_WDTs 5 -#define MMIO_DEVID_SYSREGS 6 -#define MMIO_DEVID_SE 7 -#define MMIO_DEVID_SYSCTR0 8 -#define MMIO_DEVID_MC 9 -#define MMIO_DEVID_FUSE 10 -#define MMIO_DEVID_MISC 11 -#define MMIO_DEVID_FLOWCTRL 12 -#define MMIO_DEVID_NXBOOTLOADER_MAILBOX 13 -#define MMIO_DEVID_I2C56_SPI2B 14 -#define MMIO_DEVID_GPIO 15 -#define MMIO_DEVID_DTV_I2C234 16 -#define MMIO_DEVID_EXCEPTION_VECTORS 17 -#define MMIO_DEVID_MC0 18 -#define MMIO_DEVID_MC1 19 -#define MMIO_DEVID_AMS_IRAM_PAGE 20 -#define MMIO_DEVID_AMS_USER_PAGE 21 -#define MMIO_DEVID_DEBUG_IRAM 22 -#define MMIO_DEVID_MAX 23 - -#define LP0_ENTRY_RAM_SEGMENT_ID_ENCRYPTED_TZRAM 0 -#define LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE 1 -#define LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM 2 -#define LP0_ENTRY_RAM_SEGMENT_ID_MAX 3 - -#define WARMBOOT_RAM_SEGMENT_ID_SE_STATE 0 -#define WARMBOOT_RAM_SEGMENT_ID_TZRAM 1 -#define WARMBOOT_RAM_SEGMENT_ID_MAX 2 - -#define TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN 0 -#define TZRAM_SEGMENT_ID_PK2LDR 1 -#define TZRAM_SEGMENT_ID_USERPAGE 2 -#define TZRAM_SEGMENT_ID_CORE012_STACK 3 -#define TZRAM_SEGMENT_ID_CORE3_STACK 4 -#define TZRAM_SEGEMENT_ID_SECMON_EVT 5 -#define TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE 6 -#define TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE 7 -#define TZRAM_SEGMENT_ID_MAX 8 - -#define IDENTITY_GET_MAPPING_ADDRESS(mapping_id) (TUPLE_ELEM_0(CAT(_MMAPID, EVAL(mapping_id)))) -#define IDENTITY_GET_MAPPING_SIZE(mapping_id) (TUPLE_ELEM_1(CAT(_MMAPID, EVAL(mapping_id)))) -#define IDENTITY_GET_MAPPING_ATTRIBS(mapping_id) (TUPLE_ELEM_2(CAT(_MMAPID, EVAL(mapping_id)))) -#define IDENTITY_IS_MAPPING_BLOCK_RANGE(mapping_id) (TUPLE_ELEM_3(CAT(_MMAPID, EVAL(mapping_id)))) - -#define MMIO_GET_DEVICE_PA(device_id) (TUPLE_ELEM_0(CAT(_MMAPDEV, EVAL(device_id)))) -#define MMIO_GET_DEVICE_7X_PA(device_id) (TUPLE_ELEM_0(CAT(_MMAPDEV, EVAL(device_id)))) -#define MMIO_GET_DEVICE_ADDRESS(device_id)\ -(\ - (TUPLE_FOLD_LEFT_1(EVAL(device_id), _MMAPDEV, PLUS) EVAL(MMIO_BASE)) +\ - 0x1000ull * (device_id)\ -) -#define MMIO_GET_DEVICE_SIZE(device_id) (TUPLE_ELEM_1(CAT(_MMAPDEV, EVAL(device_id)))) -#define MMIO_IS_DEVICE_SECURE(device_id) (TUPLE_ELEM_2(CAT(_MMAPDEV, EVAL(device_id)))) - -#define LP0_ENTRY_GET_RAM_SEGMENT_PA(segment_id) (TUPLE_ELEM_0(CAT(_MMAPLP0ES, EVAL(segment_id)))) -#define LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(segment_id) (EVAL(LP0_ENTRY_RAM_SEGMENT_BASE) + 0x10000ull * (segment_id)) -#define LP0_ENTRY_GET_RAM_SEGMENT_SIZE(segment_id) (TUPLE_ELEM_1(CAT(_MMAPLP0ES, EVAL(segment_id)))) -#define LP0_ENTRY_GET_RAM_SEGMENT_ATTRIBS(segment_id) (TUPLE_ELEM_2(CAT(_MMAPLP0ES, EVAL(segment_id)))) - -#define WARMBOOT_GET_RAM_SEGMENT_PA(segment_id) (TUPLE_ELEM_0(CAT(_MMAPWBS, EVAL(segment_id)))) -#define WARMBOOT_GET_RAM_SEGMENT_ADDRESS(segment_id) (TUPLE_FOLD_LEFT_1(EVAL(segment_id), _MMAPWBS, PLUS) EVAL(WARMBOOT_RAM_SEGMENT_BASE)) -#define WARMBOOT_GET_RAM_SEGMENT_SIZE(segment_id) (TUPLE_ELEM_1(CAT(_MMAPWBS, EVAL(segment_id)))) -#define WARMBOOT_GET_RAM_SEGMENT_ATTRIBS(segment_id) (TUPLE_ELEM_2(CAT(_MMAPWBS, EVAL(segment_id)))) - -#define TZRAM_GET_SEGMENT_PA(segment_id) (0x7C010000ull + (TUPLE_ELEM_0(CAT(_MMAPTZS, EVAL(segment_id))))) -#define TZRAM_GET_SEGMENT_5X_PA(segment_id) (0x7C010000ull + (TUPLE_ELEM_0(CAT(_MMAPTZ5XS, EVAL(segment_id))))) -#define TZRAM_GET_SEGMENT_ADDRESS(segment_id) (TUPLE_FOLD_LEFT_2(EVAL(segment_id), _MMAPTZS, PLUS) EVAL(TZRAM_SEGMENT_BASE)) -#define TZRAM_GET_SEGMENT_SIZE(segment_id) (TUPLE_ELEM_1(CAT(_MMAPTZS, EVAL(segment_id)))) -#define TZRAM_IS_SEGMENT_EXECUTABLE(segment_id) (TUPLE_ELEM_3(CAT(_MMAPTZS, EVAL(segment_id)))) - -/**********************************************************************************************/ -/* We don't need unmapping functions */ - -ALINLINE static inline void identity_map_mapping(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl, uintptr_t addr, size_t size, uint64_t attribs, bool is_block_range) { - if (is_block_range) { - mmu_map_block_range(1, mmu_l1_tbl, addr, addr, size, attribs | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL); - } - else { - mmu_map_page_range(mmu_l3_tbl, addr, addr, size, attribs | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL); - } -} -ALINLINE static inline void mmio_map_device(uintptr_t *mmu_l3_tbl, uintptr_t addr, uintptr_t pa, size_t size, bool is_secure) { - static const uint64_t secure_device_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_DEVICE; - static const uint64_t device_attributes = MMU_PTE_BLOCK_NS | secure_device_attributes; - - uint64_t attributes = is_secure ? secure_device_attributes : device_attributes; - mmu_map_page_range(mmu_l3_tbl, addr, pa, size, attributes); -} - -ALINLINE static inline void lp0_entry_map_ram_segment(uintptr_t *mmu_l3_tbl, uintptr_t addr, uintptr_t pa, size_t size, uint64_t attribs) { - uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | attribs; - - mmu_map_page_range(mmu_l3_tbl, addr, pa, size, attributes); -} - -ALINLINE static inline void warmboot_map_ram_segment(uintptr_t *mmu_l3_tbl, uintptr_t addr, uintptr_t pa, size_t size, uint64_t attribs) { - uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | attribs; - - mmu_map_page_range(mmu_l3_tbl, addr, pa, size, attributes); -} - -ALINLINE static inline void tzram_map_segment(uintptr_t *mmu_l3_tbl, uintptr_t addr, uintptr_t pa, size_t size, bool is_executable) { - uint64_t attributes = (is_executable ? 0 : MMU_PTE_BLOCK_XN) | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL; - - mmu_map_page_range(mmu_l3_tbl, addr, pa, size, attributes); -} - -#endif diff --git a/exosphere/src/misc.h b/exosphere/src/misc.h deleted file mode 100644 index 40b633c4b..000000000 --- a/exosphere/src/misc.h +++ /dev/null @@ -1,32 +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_MISC_H -#define EXOSPHERE_MISC_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 MISC Registers. */ - -#define MISC_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MISC)) -#define MAKE_MISC_REG(n) MAKE_REG32(MISC_BASE + n) - -#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 MAKE_MISC_REG(0x0C00) -#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 MAKE_MISC_REG(0x0C04) -#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 MAKE_MISC_REG(0x0C08) - -#endif diff --git a/exosphere/src/mmu.h b/exosphere/src/mmu.h deleted file mode 100644 index ed4cba784..000000000 --- a/exosphere/src/mmu.h +++ /dev/null @@ -1,193 +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_MMU_H -#define EXOSPHERE_MMU_H - -#include <stdbool.h> -#include <stdint.h> -#include <stddef.h> -#include "utils.h" - - -#ifndef MMU_GRANULE_TYPE -#define MMU_GRANULE_TYPE 0 /* 0: 4KB, 1: 64KB, 2: 16KB. The Switch always uses a 4KB granule size. */ -#endif - -#if MMU_GRANULE_TYPE == 0 -#define MMU_Lx_SHIFT(x) (12 + 9 * (3 - (x))) -#define MMU_Lx_MASK(x) MASKL(9) -#elif MMU_GRANULE_TYPE == 1 -/* 64 KB, no L0 here */ -#define MMU_Lx_SHIFT(x) (16 + 13 * (3 - (x))) -#define MMU_Lx_MASK(x) ((x) == 1 ? MASKL(5) : MASKL(13)) -#elif MMU_GRANULE_TYPE == 2 -#define MMU_Lx_SHIFT(x) (14 + 11 * (3 - (x))) -#define MMU_Lx_MASK(x) ((x) == 0 ? 1 : MASKL(11)) -#endif - -/* - * The following defines are adapted from uboot: - * - * (C) Copyright 2013 - * David Feng <fenghua@phytium.com.cn> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* Memory attributes, see set_memory_registers_enable_mmu */ -#define MMU_MT_NORMAL 0ull -#define MMU_MT_DEVICE_NGNRE 1ull -#define MMU_MT_DEVICE_NGNRNE 2ull /* not used, also the same as Attr4-7 */ - -/* - * Hardware page table definitions. - * - */ - -#define MMU_PTE_TYPE_MASK 3ull -#define MMU_PTE_TYPE_FAULT 0ull -#define MMU_PTE_TYPE_TABLE 3ull -#define MMU_PTE_TYPE_BLOCK 1ull - -/* L3 only */ -#define MMU_PTE_TYPE_PAGE 3ull - -#define MMU_PTE_TABLE_PXN BITL(59) -#define MMU_PTE_TABLE_XN BITL(60) -#define MMU_PTE_TABLE_AP BITL(61) -#define MMU_PTE_TABLE_NS BITL(63) - -/* - * Block - */ -#define MMU_PTE_BLOCK_MEMTYPE(x) ((uint64_t)((x) << 2)) -#define MMU_PTE_BLOCK_NS BITL(5) -#define MMU_PTE_BLOCK_NON_SHAREABLE (0ull << 8) -#define MMU_PTE_BLOCK_OUTER_SHAREABLE (2ull << 8) -#define MMU_PTE_BLOCK_INNER_SHAREBLE (3ull << 8) -#define MMU_PTE_BLOCK_AF BITL(10) -#define MMU_PTE_BLOCK_NG BITL(11) -#define MMU_PTE_BLOCK_PXN BITL(53) -#define MMU_PTE_BLOCK_UXN BITL(54) -#define MMU_PTE_BLOCK_XN MMU_PTE_BLOCK_UXN - -/* - * AP[2:1] - */ -#define MMU_AP_PRIV_RW (0ull << 6) -#define MMU_AP_RW (1ull << 6) -#define MMU_AP_PRIV_RO (2ull << 6) -#define MMU_AP_RO (3ull << 6) - -/* - * S2AP[2:1] (for stage2 translations; secmon doesn't use it) - */ -#define MMU_S2AP_NONE (0ull << 6) -#define MMU_S2AP_RO (1ull << 6) -#define MMU_S2AP_WO (2ull << 6) -#define MMU_S2AP_RW (3ull << 6) - -/* - * AttrIndx[2:0] - */ -#define MMU_PMD_ATTRINDX(t) ((uint64_t)((t) << 2)) -#define MMU_PMD_ATTRINDX_MASK (7ull << 2) - -/* - * TCR flags. - */ -#define TCR_T0SZ(x) ((64 - (x)) << 0) -#define TCR_IRGN_NC (0 << 8) -#define TCR_IRGN_WBWA (1 << 8) -#define TCR_IRGN_WT (2 << 8) -#define TCR_IRGN_WBNWA (3 << 8) -#define TCR_IRGN_MASK (3 << 8) -#define TCR_ORGN_NC (0 << 10) -#define TCR_ORGN_WBWA (1 << 10) -#define TCR_ORGN_WT (2 << 10) -#define TCR_ORGN_WBNWA (3 << 10) -#define TCR_ORGN_MASK (3 << 10) -#define TCR_NOT_SHARED (0 << 12) -#define TCR_SHARED_OUTER (2 << 12) -#define TCR_SHARED_INNER (3 << 12) -#define TCR_TG0_4K (0 << 14) -#define TCR_TG0_64K (1 << 14) -#define TCR_TG0_16K (2 << 14) -#define TCR_PS(x) ((x) << 16) -#define TCR_EPD1_DISABLE BIT(23) - -#define TCR_EL1_RSVD BIT(31) -#define TCR_EL2_RSVD (BIT(31) | BIT(23)) -#define TCR_EL3_RSVD (BIT(31) | BIT(23)) - -static inline void mmu_init_table(volatile uintptr_t *tbl, size_t num_entries) { - for(size_t i = 0; i < num_entries / 8; i++) { - tbl[i] = MMU_PTE_TYPE_FAULT; - } -} - -/* - All the functions below assume base_addr is valid. - They do not invalidate the TLB, which must be done separately. -*/ - -static inline unsigned int mmu_compute_index(unsigned int level, uintptr_t base_addr) { - return (base_addr >> MMU_Lx_SHIFT(level)) & MMU_Lx_MASK(level); -} - -static inline void mmu_map_table(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t *next_lvl_tbl_pa, uint64_t attrs) { - tbl[mmu_compute_index(level, base_addr)] = (uintptr_t)next_lvl_tbl_pa | attrs | MMU_PTE_TYPE_TABLE; -} - -static inline void mmu_map_block(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { - tbl[mmu_compute_index(level, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_BLOCK; -} - -static inline void mmu_map_page(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { - tbl[mmu_compute_index(3, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_PAGE; -} - -static inline void mmu_unmap(unsigned int level, uintptr_t *tbl, uintptr_t base_addr) { - tbl[mmu_compute_index(level, base_addr)] = MMU_PTE_TYPE_FAULT; -} - -static inline void mmu_unmap_page(uintptr_t *tbl, uintptr_t base_addr) { - tbl[mmu_compute_index(3, base_addr)] = MMU_PTE_TYPE_FAULT; -} - -static inline void mmu_map_block_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { - size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); - for(size_t offset = 0; offset < size; offset += BITL(MMU_Lx_SHIFT(level))) { - mmu_map_block(level, tbl, base_addr + offset, phys_addr + offset, attrs); - } -} - -static inline void mmu_map_page_range(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { - size = ((size + (BITL(MMU_Lx_SHIFT(3)) - 1)) >> MMU_Lx_SHIFT(3)) << MMU_Lx_SHIFT(3); - for(size_t offset = 0; offset < size; offset += BITL(MMU_Lx_SHIFT(3))) { - mmu_map_page(tbl, base_addr + offset, phys_addr + offset, attrs); - } -} - -static inline void mmu_unmap_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, size_t size) { - size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); - for(size_t offset = 0; offset < size; offset += BITL(MMU_Lx_SHIFT(level))) { - mmu_unmap(level, tbl, base_addr + offset); - } -} - -#endif diff --git a/exosphere/src/my_libc.c b/exosphere/src/my_libc.c deleted file mode 100644 index ffd1b595b..000000000 --- a/exosphere/src/my_libc.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* Note: copied from newlib */ -#ifdef __cplusplus -extern "C" { -#endif - -#include <string.h> -#include <stddef.h> -#include <limits.h> - -/* - * Copyright (C) 2004 CodeSourcery, LLC - * - * Permission to use, copy, modify, and distribute this file - * for any purpose is hereby granted without fee, provided that - * the above copyright notice and this notice appears in all - * copies. - * - * This file is distributed WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - -/* Handle ELF .{pre_init,init,fini}_array sections. */ -#include <sys/types.h> - -#ifndef HAVE_INITFINI_ARRAY -#define HAVE_INITFINI_ARRAY -#endif - -#undef HAVE_INIT_FINI - -#ifdef HAVE_INITFINI_ARRAY - -/* These magic symbols are provided by the linker. */ -extern void (*__preinit_array_start []) (void) __attribute__((weak)); -extern void (*__preinit_array_end []) (void) __attribute__((weak)); -extern void (*__init_array_start []) (void) __attribute__((weak)); -extern void (*__init_array_end []) (void) __attribute__((weak)); - -#ifdef HAVE_INIT_FINI -extern void _init (void); -#endif - -/* Iterate over all the init routines. */ -void -__libc_init_array (void) -{ - size_t count; - size_t i; - - count = __preinit_array_end - __preinit_array_start; - for (i = 0; i < count; i++) - __preinit_array_start[i] (); - -#ifdef HAVE_INIT_FINI - _init (); -#endif - - count = __init_array_end - __init_array_start; - for (i = 0; i < count; i++) - __init_array_start[i] (); -} -#endif - -#ifdef HAVE_INITFINI_ARRAY -extern void (*__fini_array_start []) (void) __attribute__((weak)); -extern void (*__fini_array_end []) (void) __attribute__((weak)); - -#ifdef HAVE_INIT_FINI -extern void _fini (void); -#endif - -/* Run all the cleanup routines. */ -void -__libc_fini_array (void) -{ - size_t count; - size_t i; - - count = __fini_array_end - __fini_array_start; - for (i = count; i > 0; i--) - __fini_array_start[i-1] (); - -#ifdef HAVE_INIT_FINI - _fini (); -#endif -} -#endif - -/* -FUNCTION - <<memmove>>---move possibly overlapping memory -INDEX - memmove -SYNOPSIS - #include <string.h> - void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>); -DESCRIPTION - This function moves <[length]> characters from the block of - memory starting at <<*<[src]>>> to the memory starting at - <<*<[dst]>>>. <<memmove>> reproduces the characters correctly - at <<*<[dst]>>> even if the two areas overlap. -RETURNS - The function returns <[dst]> as passed. -PORTABILITY -<<memmove>> is ANSI C. -<<memmove>> requires no supporting OS subroutines. -QUICKREF - memmove ansi pure -*/ - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -/* How many bytes are copied each iteration of the 4X unrolled loop. */ -#define BIGBLOCKSIZE (sizeof (long) << 2) - -/* How many bytes are copied each iteration of the word copy loop. */ -#define LITTLEBLOCKSIZE (sizeof (long)) - -/* Threshhold for punting to the byte copier. */ -#undef TOO_SMALL -#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) - -/*SUPPRESS 20*/ -void * -//__inhibit_loop_to_libcall -memmove (void *dst_void, - const void *src_void, - size_t length) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - char *dst = dst_void; - const char *src = src_void; - - if (src < dst && dst < src + length) - { - /* Have to copy backwards */ - src += length; - dst += length; - while (length--) - { - *--dst = *--src; - } - } - else - { - while (length--) - { - *dst++ = *src++; - } - } - - return dst_void; -#else - char *dst = dst_void; - const char *src = src_void; - long *aligned_dst; - const long *aligned_src; - - if (src < dst && dst < src + length) - { - /* Destructive overlap...have to copy backwards */ - src += length; - dst += length; - while (length--) - { - *--dst = *--src; - } - } - else - { - /* Use optimizing algorithm for a non-destructive copy to closely - match memcpy. If the size is small or either SRC or DST is unaligned, - then punt into the byte copy loop. This should be rare. */ - if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) - { - aligned_dst = (long*)dst; - aligned_src = (long*)src; - - /* Copy 4X long words at a time if possible. */ - while (length >= BIGBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - length -= BIGBLOCKSIZE; - } - - /* Copy one long word at a time if possible. */ - while (length >= LITTLEBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - length -= LITTLEBLOCKSIZE; - } - - /* Pick up any residual with a byte copier. */ - dst = (char*)aligned_dst; - src = (char*)aligned_src; - } - - while (length--) - { - *dst++ = *src++; - } - } - - return dst_void; -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<memcpy>>---copy memory regions -SYNOPSIS - #include <string.h> - void* memcpy(void *restrict <[out]>, const void *restrict <[in]>, - size_t <[n]>); -DESCRIPTION - This function copies <[n]> bytes from the memory region - pointed to by <[in]> to the memory region pointed to by - <[out]>. - If the regions overlap, the behavior is undefined. -RETURNS - <<memcpy>> returns a pointer to the first byte of the <[out]> - region. -PORTABILITY -<<memcpy>> is ANSI C. -<<memcpy>> requires no supporting OS subroutines. -QUICKREF - memcpy ansi pure - */ - -void * -memcpy (void * dst0, - const void * __restrict src0, - size_t len0) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - char *dst = (char *) dst0; - char *src = (char *) src0; - - void *save = dst0; - - while (len0--) - { - *dst++ = *src++; - } - - return save; -#else - char *dst = dst0; - const char *src = src0; - long *aligned_dst; - const long *aligned_src; - - /* If the size is small, or either SRC or DST is unaligned, - then punt into the byte copy loop. This should be rare. */ - if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) - { - aligned_dst = (long*)dst; - aligned_src = (long*)src; - - /* Copy 4X long words at a time if possible. */ - while (len0 >= BIGBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - *aligned_dst++ = *aligned_src++; - len0 -= BIGBLOCKSIZE; - } - - /* Copy one long word at a time if possible. */ - while (len0 >= LITTLEBLOCKSIZE) - { - *aligned_dst++ = *aligned_src++; - len0 -= LITTLEBLOCKSIZE; - } - - /* Pick up any residual with a byte copier. */ - dst = (char*)aligned_dst; - src = (char*)aligned_src; - } - - while (len0--) - *dst++ = *src++; - - return dst0; -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<memset>>---set an area of memory -INDEX - memset -SYNOPSIS - #include <string.h> - void *memset(void *<[dst]>, int <[c]>, size_t <[length]>); -DESCRIPTION - This function converts the argument <[c]> into an unsigned - char and fills the first <[length]> characters of the array - pointed to by <[dst]> to the value. -RETURNS - <<memset>> returns the value of <[dst]>. -PORTABILITY -<<memset>> is ANSI C. - <<memset>> requires no supporting OS subroutines. -QUICKREF - memset ansi pure -*/ - -#include <string.h> - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -#define LBLOCKSIZE (sizeof(long)) -#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) -#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) - -void * -memset (void *m, - int c, - size_t n) -{ - char *s = (char *) m; - -#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) - unsigned int i; - unsigned long buffer; - unsigned long *aligned_addr; - unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an - unsigned variable. */ - - while (UNALIGNED (s)) - { - if (n--) - *s++ = (char) c; - else - return m; - } - - if (!TOO_SMALL (n)) - { - /* If we get this far, we know that n is large and s is word-aligned. */ - aligned_addr = (unsigned long *) s; - - /* Store D into each char sized location in BUFFER so that - we can set large blocks quickly. */ - buffer = (d << 8) | d; - buffer |= (buffer << 16); - for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) - buffer = (buffer << i) | buffer; - - /* Unroll the loop. */ - while (n >= LBLOCKSIZE*4) - { - *aligned_addr++ = buffer; - *aligned_addr++ = buffer; - *aligned_addr++ = buffer; - *aligned_addr++ = buffer; - n -= 4*LBLOCKSIZE; - } - - while (n >= LBLOCKSIZE) - { - *aligned_addr++ = buffer; - n -= LBLOCKSIZE; - } - /* Pick up the remainder with a bytewise loop. */ - s = (char*)aligned_addr; - } - -#endif /* not PREFER_SIZE_OVER_SPEED */ - - while (n--) - *s++ = (char) c; - - return m; -} - -/* -FUNCTION - <<memchr>>---find character in memory -INDEX - memchr -SYNOPSIS - #include <string.h> - void *memchr(const void *<[src]>, int <[c]>, size_t <[length]>); -DESCRIPTION - This function searches memory starting at <<*<[src]>>> for the - character <[c]>. The search only ends with the first - occurrence of <[c]>, or after <[length]> characters; in - particular, <<NUL>> does not terminate the search. -RETURNS - If the character <[c]> is found within <[length]> characters - of <<*<[src]>>>, a pointer to the character is returned. If - <[c]> is not found, then <<NULL>> is returned. -PORTABILITY -<<memchr>> is ANSI C. -<<memchr>> requires no supporting OS subroutines. -QUICKREF - memchr ansi pure -*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) - -/* How many bytes are loaded each iteration of the word copy loop. */ -#define LBLOCKSIZE (sizeof (long)) - -/* Threshhold for punting to the bytewise iterator. */ -#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) - -#if LONG_MAX == 2147483647L -#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) -#else -#if LONG_MAX == 9223372036854775807L -/* Nonzero if X (a long int) contains a NULL byte. */ -#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) -#else -#error long int is not a 32bit or 64bit type. -#endif -#endif - -#ifndef DETECTNULL -#error long int is not a 32bit or 64bit byte -#endif - -/* DETECTCHAR returns nonzero if (long)X contains the byte used - to fill (long)MASK. */ -#define DETECTCHAR(X,MASK) (DETECTNULL(X ^ MASK)) - -void * -memchr (const void *src_void, - int c, - size_t length) -{ - const unsigned char *src = (const unsigned char *) src_void; - unsigned char d = c; - -#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) - unsigned long *asrc; - unsigned long mask; - unsigned int i; - - while (UNALIGNED (src)) - { - if (!length--) - return NULL; - if (*src == d) - return (void *) src; - src++; - } - - if (!TOO_SMALL (length)) - { - /* If we get this far, we know that length is large and src is - word-aligned. */ - /* The fast code reads the source one word at a time and only - performs the bytewise search on word-sized segments if they - contain the search character, which is detected by XORing - the word-sized segment with a word-sized block of the search - character and then detecting for the presence of NUL in the - result. */ - asrc = (unsigned long *) src; - mask = d << 8 | d; - mask = mask << 16 | mask; - for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) - mask = (mask << i) | mask; - - while (length >= LBLOCKSIZE) - { - if (DETECTCHAR (*asrc, mask)) - break; - length -= LBLOCKSIZE; - asrc++; - } - - /* If there are fewer than LBLOCKSIZE characters left, - then we resort to the bytewise loop. */ - - src = (unsigned char *) asrc; - } - -#endif /* not PREFER_SIZE_OVER_SPEED */ - - while (length--) - { - if (*src == d) - return (void *) src; - src++; - } - - return NULL; -} - -/* -FUNCTION - <<memcmp>>---compare two memory areas -INDEX - memcmp -SYNOPSIS - #include <string.h> - int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>); -DESCRIPTION - This function compares not more than <[n]> characters of the - object pointed to by <[s1]> with the object pointed to by <[s2]>. -RETURNS - The function returns an integer greater than, equal to or - less than zero according to whether the object pointed to by - <[s1]> is greater than, equal to or less than the object - pointed to by <[s2]>. -PORTABILITY -<<memcmp>> is ANSI C. -<<memcmp>> requires no supporting OS subroutines. -QUICKREF - memcmp ansi pure -*/ - - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -/* How many bytes are copied each iteration of the word copy loop. */ -#define LBLOCKSIZE (sizeof (long)) - -/* Threshhold for punting to the byte copier. */ -#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) - -int -memcmp (const void *m1, - const void *m2, - size_t n) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - unsigned char *s1 = (unsigned char *) m1; - unsigned char *s2 = (unsigned char *) m2; - - while (n--) - { - if (*s1 != *s2) - { - return *s1 - *s2; - } - s1++; - s2++; - } - return 0; -#else - unsigned char *s1 = (unsigned char *) m1; - unsigned char *s2 = (unsigned char *) m2; - unsigned long *a1; - unsigned long *a2; - - /* If the size is too small, or either pointer is unaligned, - then we punt to the byte compare loop. Hopefully this will - not turn up in inner loops. */ - if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) - { - /* Otherwise, load and compare the blocks of memory one - word at a time. */ - a1 = (unsigned long*) s1; - a2 = (unsigned long*) s2; - while (n >= LBLOCKSIZE) - { - if (*a1 != *a2) - break; - a1++; - a2++; - n -= LBLOCKSIZE; - } - - /* check m mod LBLOCKSIZE remaining characters */ - - s1 = (unsigned char*)a1; - s2 = (unsigned char*)a2; - } - - while (n--) - { - if (*s1 != *s2) - return *s1 - *s2; - s1++; - s2++; - } - - return 0; -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<strchr>>---search for character in string -INDEX - strchr -SYNOPSIS - #include <string.h> - char * strchr(const char *<[string]>, int <[c]>); -DESCRIPTION - This function finds the first occurence of <[c]> (converted to - a char) in the string pointed to by <[string]> (including the - terminating null character). -RETURNS - Returns a pointer to the located character, or a null pointer - if <[c]> does not occur in <[string]>. -PORTABILITY -<<strchr>> is ANSI C. -<<strchr>> requires no supporting OS subroutines. -QUICKREF - strchr ansi pure -*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - - -/* Nonzero if X is not aligned on a "long" boundary. */ -#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) - -/* How many bytes are loaded each iteration of the word copy loop. */ -#define LBLOCKSIZE (sizeof (long)) - -char * -strchr (const char *s1, - int i) -{ - const unsigned char *s = (const unsigned char *)s1; - unsigned char c = i; - -#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) - unsigned long mask,j; - unsigned long *aligned_addr; - - /* Special case for finding 0. */ - if (!c) - { - while (UNALIGNED (s)) - { - if (!*s) - return (char *) s; - s++; - } - /* Operate a word at a time. */ - aligned_addr = (unsigned long *) s; - while (!DETECTNULL (*aligned_addr)) - aligned_addr++; - /* Found the end of string. */ - s = (const unsigned char *) aligned_addr; - while (*s) - s++; - return (char *) s; - } - - /* All other bytes. Align the pointer, then search a long at a time. */ - while (UNALIGNED (s)) - { - if (!*s) - return NULL; - if (*s == c) - return (char *) s; - s++; - } - - mask = c; - for (j = 8; j < LBLOCKSIZE * 8; j <<= 1) - mask = (mask << j) | mask; - - aligned_addr = (unsigned long *) s; - while (!DETECTNULL (*aligned_addr) && !DETECTCHAR (*aligned_addr, mask)) - aligned_addr++; - - /* The block of bytes currently pointed to by aligned_addr - contains either a null or the target char, or both. We - catch it using the bytewise search. */ - - s = (unsigned char *) aligned_addr; - -#endif /* not PREFER_SIZE_OVER_SPEED */ - - while (*s && *s != c) - s++; - if (*s == c) - return (char *)s; - return NULL; -} - -/* -FUNCTION - <<strcmp>>---character string compare - -INDEX - strcmp -SYNOPSIS - #include <string.h> - int strcmp(const char *<[a]>, const char *<[b]>); -DESCRIPTION - <<strcmp>> compares the string at <[a]> to - the string at <[b]>. -RETURNS - If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, - <<strcmp>> returns a number greater than zero. If the two - strings match, <<strcmp>> returns zero. If <<*<[a]>>> - sorts lexicographically before <<*<[b]>>>, <<strcmp>> returns a - number less than zero. -PORTABILITY -<<strcmp>> is ANSI C. -<<strcmp>> requires no supporting OS subroutines. -QUICKREF - strcmp ansi pure -*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -int -strcmp (const char *s1, - const char *s2) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - while (*s1 != '\0' && *s1 == *s2) - { - s1++; - s2++; - } - - return (*(unsigned char *) s1) - (*(unsigned char *) s2); -#else - unsigned long *a1; - unsigned long *a2; - - /* If s1 or s2 are unaligned, then compare bytes. */ - if (!UNALIGNED (s1, s2)) - { - /* If s1 and s2 are word-aligned, compare them a word at a time. */ - a1 = (unsigned long*)s1; - a2 = (unsigned long*)s2; - while (*a1 == *a2) - { - /* To get here, *a1 == *a2, thus if we find a null in *a1, - then the strings must be equal, so return zero. */ - if (DETECTNULL (*a1)) - return 0; - - a1++; - a2++; - } - - /* A difference was detected in last few bytes of s1, so search bytewise */ - s1 = (char*)a1; - s2 = (char*)a2; - } - - while (*s1 != '\0' && *s1 == *s2) - { - s1++; - s2++; - } - return (*(unsigned char *) s1) - (*(unsigned char *) s2); -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<strcpy>>---copy string -INDEX - strcpy -SYNOPSIS - #include <string.h> - char *strcpy(char *<[dst]>, const char *<[src]>); -DESCRIPTION - <<strcpy>> copies the string pointed to by <[src]> - (including the terminating null character) to the array - pointed to by <[dst]>. -RETURNS - This function returns the initial value of <[dst]>. -PORTABILITY -<<strcpy>> is ANSI C. -<<strcpy>> requires no supporting OS subroutines. -QUICKREF - strcpy ansi pure -*/ - -/*SUPPRESS 560*/ -/*SUPPRESS 530*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -/* Nonzero if either X or Y is not aligned on a "long" boundary. */ -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -char* -strcpy (char *dst0, - const char *src0) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - char *s = dst0; - - while ((*dst0++ = *src0++)) - ; - - return s; -#else - char *dst = dst0; - const char *src = src0; - long *aligned_dst; - const long *aligned_src; - - /* If SRC or DEST is unaligned, then copy bytes. */ - if (!UNALIGNED (src, dst)) - { - aligned_dst = (long*)dst; - aligned_src = (long*)src; - - /* SRC and DEST are both "long int" aligned, try to do "long int" - sized copies. */ - while (!DETECTNULL(*aligned_src)) - { - *aligned_dst++ = *aligned_src++; - } - - dst = (char*)aligned_dst; - src = (char*)aligned_src; - } - - while ((*dst++ = *src++)) - ; - return dst0; -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<strlen>>---character string length -INDEX - strlen -SYNOPSIS - #include <string.h> - size_t strlen(const char *<[str]>); -DESCRIPTION - The <<strlen>> function works out the length of the string - starting at <<*<[str]>>> by counting chararacters until it - reaches a <<NULL>> character. -RETURNS - <<strlen>> returns the character count. -PORTABILITY -<<strlen>> is ANSI C. -<<strlen>> requires no supporting OS subroutines. -QUICKREF - strlen ansi pure -*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -#define LBLOCKSIZE (sizeof (long)) -#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) -size_t -strlen (const char *str) -{ - const char *start = str; - -#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) - unsigned long *aligned_addr; - - /* Align the pointer, so we can search a word at a time. */ - while (UNALIGNED (str)) - { - if (!*str) - return str - start; - str++; - } - - /* If the string is word-aligned, we can check for the presence of - a null in each word-sized block. */ - aligned_addr = (unsigned long *)str; - while (!DETECTNULL (*aligned_addr)) - aligned_addr++; - - /* Once a null is detected, we check each byte in that block for a - precise position of the null. */ - str = (char *) aligned_addr; - -#endif /* not PREFER_SIZE_OVER_SPEED */ - - while (*str) - str++; - return str - start; -} - -/* -FUNCTION - <<strncmp>>---character string compare - -INDEX - strncmp -SYNOPSIS - #include <string.h> - int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>); -DESCRIPTION - <<strncmp>> compares up to <[length]> characters - from the string at <[a]> to the string at <[b]>. -RETURNS - If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, - <<strncmp>> returns a number greater than zero. If the two - strings are equivalent, <<strncmp>> returns zero. If <<*<[a]>>> - sorts lexicographically before <<*<[b]>>>, <<strncmp>> returns a - number less than zero. -PORTABILITY -<<strncmp>> is ANSI C. -<<strncmp>> requires no supporting OS subroutines. -QUICKREF - strncmp ansi pure -*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -int -strncmp (const char *s1, - const char *s2, - size_t n) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - if (n == 0) - return 0; - - while (n-- != 0 && *s1 == *s2) - { - if (n == 0 || *s1 == '\0') - break; - s1++; - s2++; - } - - return (*(unsigned char *) s1) - (*(unsigned char *) s2); -#else - unsigned long *a1; - unsigned long *a2; - - if (n == 0) - return 0; - - /* If s1 or s2 are unaligned, then compare bytes. */ - if (!UNALIGNED (s1, s2)) - { - /* If s1 and s2 are word-aligned, compare them a word at a time. */ - a1 = (unsigned long*)s1; - a2 = (unsigned long*)s2; - while (n >= sizeof (long) && *a1 == *a2) - { - n -= sizeof (long); - - /* If we've run out of bytes or hit a null, return zero - since we already know *a1 == *a2. */ - if (n == 0 || DETECTNULL (*a1)) - return 0; - - a1++; - a2++; - } - - /* A difference was detected in last few bytes of s1, so search bytewise */ - s1 = (char*)a1; - s2 = (char*)a2; - } - - while (n-- > 0 && *s1 == *s2) - { - /* If we've run out of bytes or hit a null, return zero - since we already know *s1 == *s2. */ - if (n == 0 || *s1 == '\0') - return 0; - s1++; - s2++; - } - return (*(unsigned char *) s1) - (*(unsigned char *) s2); -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<strncpy>>---counted copy string -INDEX - strncpy -SYNOPSIS - #include <string.h> - char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>, - size_t <[length]>); -DESCRIPTION - <<strncpy>> copies not more than <[length]> characters from the - the string pointed to by <[src]> (including the terminating - null character) to the array pointed to by <[dst]>. If the - string pointed to by <[src]> is shorter than <[length]> - characters, null characters are appended to the destination - array until a total of <[length]> characters have been - written. -RETURNS - This function returns the initial value of <[dst]>. -PORTABILITY -<<strncpy>> is ANSI C. -<<strncpy>> requires no supporting OS subroutines. -QUICKREF - strncpy ansi pure -*/ - -/*SUPPRESS 560*/ -/*SUPPRESS 530*/ - -#undef LBLOCKSIZE -#undef UNALIGNED -#undef TOO_SMALL - -#define UNALIGNED(X, Y) \ - (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) - -#define TOO_SMALL(LEN) ((LEN) < sizeof (long)) - -char * -strncpy (char *__restrict dst0, - const char *__restrict src0, - size_t count) -{ -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - char *dscan; - const char *sscan; - - dscan = dst0; - sscan = src0; - while (count > 0) - { - --count; - if ((*dscan++ = *sscan++) == '\0') - break; - } - while (count-- > 0) - *dscan++ = '\0'; - - return dst0; -#else - char *dst = dst0; - const char *src = src0; - long *aligned_dst; - const long *aligned_src; - - /* If SRC and DEST is aligned and count large enough, then copy words. */ - if (!UNALIGNED (src, dst) && !TOO_SMALL (count)) - { - aligned_dst = (long*)dst; - aligned_src = (long*)src; - - /* SRC and DEST are both "long int" aligned, try to do "long int" - sized copies. */ - while (count >= sizeof (long int) && !DETECTNULL(*aligned_src)) - { - count -= sizeof (long int); - *aligned_dst++ = *aligned_src++; - } - - dst = (char*)aligned_dst; - src = (char*)aligned_src; - } - - while (count > 0) - { - --count; - if ((*dst++ = *src++) == '\0') - break; - } - - while (count-- > 0) - *dst++ = '\0'; - - return dst0; -#endif /* not PREFER_SIZE_OVER_SPEED */ -} - -/* -FUNCTION - <<strnlen>>---character string length - -INDEX - strnlen -SYNOPSIS - #include <string.h> - size_t strnlen(const char *<[str]>, size_t <[n]>); -DESCRIPTION - The <<strnlen>> function works out the length of the string - starting at <<*<[str]>>> by counting chararacters until it - reaches a NUL character or the maximum: <[n]> number of - characters have been inspected. -RETURNS - <<strnlen>> returns the character count or <[n]>. -PORTABILITY -<<strnlen>> is a GNU extension. -<<strnlen>> requires no supporting OS subroutines. -*/ - -size_t -strnlen (const char *str, - size_t n) -{ - const char *start = str; - - while (n-- > 0 && *str) - str++; - - return str - start; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c deleted file mode 100644 index bee6a60f0..000000000 --- a/exosphere/src/package2.c +++ /dev/null @@ -1,637 +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 <string.h> - -#include "utils.h" -#include "memory_map.h" -#include "bootup.h" -#include "cpu_context.h" -#include "package2.h" -#include "configitem.h" -#include "se.h" -#include "interrupt.h" -#include "masterkey.h" -#include "arm.h" -#include "pmc.h" -#include "randomcache.h" -#include "timers.h" -#include "bootconfig.h" -#include "sysctr0.h" -#include "exocfg.h" -#include "smc_api.h" - -extern void *__start_cold_addr; -extern size_t __bin_size; - -static const uint8_t new_device_key_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { - {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.x New Device Key Source. */ - {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.x New Device Key Source. */ - {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.x New Device Key Source. */ - {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 New Device Key Source. */ - {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 New Device Key Source. */ - {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 New Device Key Source. */ - {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 New Device Key Source. */ - {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 New Device Key Source. */ -}; - -static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { - {0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.x New Device Keygen Source. */ - {0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.x New Device Keygen Source. */ - {0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.x New Device Keygen Source. */ - {0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 New Device Keygen Source. */ - {0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 New Device Keygen Source. */ - {0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 New Device Keygen Source. */ - {0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 New Device Keygen Source. */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ -}; - -static const uint8_t new_device_keygen_sources_dev[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { - {0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34}, /* 4.x New Device Keygen Source. */ - {0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8}, /* 5.x New Device Keygen Source. */ - {0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.x New Device Keygen Source. */ - {0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 New Device Keygen Source. */ - {0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 New Device Keygen Source. */ - {0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 New Device Keygen Source. */ - {0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 New Device Keygen Source. */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* TODO: 9.1.0 New Device Keygen Source to be added on next change-of-keys. */ -}; - -static void derive_new_device_keys(unsigned int keygen_keyslot) { - uint8_t work_buffer[0x10]; - bool is_retail = configitem_is_retail(); - for (unsigned int revision = 0; revision < MASTERKEY_NUM_NEW_DEVICE_KEYS; revision++) { - const unsigned int relative_revision = revision + MASTERKEY_REVISION_400_410; - - se_aes_ecb_decrypt_block(keygen_keyslot, work_buffer, 0x10, new_device_key_sources[revision], 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, mkey_get_keyslot(0), is_retail ? new_device_keygen_sources[revision] : new_device_keygen_sources_dev[revision], 0x10); - if (relative_revision > mkey_get_revision()) { - break; - } else if (relative_revision == mkey_get_revision()) { - /* On 7.0.0, sept will have derived this key for us already. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { - decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10); - } - } else { - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10, work_buffer, 0x10); - set_old_devkey(relative_revision, work_buffer); - } - } - set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); - clear_aes_keyslot(keygen_keyslot); -} - -/* Hardware init, sets up the RNG and SESSION keyslots, derives new DEVICE key. */ -static void setup_se(void) { - uint8_t work_buffer[0x10]; - - /* Sanity check the Security Engine. */ - se_verify_flags_cleared(); - - /* Initialize interrupts. */ - intr_initialize_gic_nonsecure(); - - /* Perform some sanity initialization. */ - volatile tegra_se_t *se = se_get_regs(); - se->SE_SE_SECURITY &= 0xFFFEFFFF; /* Clear bit 16. */ - (void)(se->SE_STATUS); - __dsb_sy(); - - /* NOTE: On 8.1.0+, Nintendo does not make keyslots 0-5 unreadable. */ - se->SE_TZRAM_SECURITY = 0; - se->SE_CRYPTO_SECURITY_PERKEY = 0; - se->SE_RSA_SECURITY_PERKEY = 0; - se->SE_SE_SECURITY &= 0xFFFFFFFB; - - /* Currently unknown what each flag does. */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - set_aes_keyslot_flags(i, 0x15); - } - - for (unsigned int i = 4; i < KEYSLOT_AES_MAX; i++) { - set_aes_keyslot_flags(i, 0x40); - } - - for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { - set_rsa_keyslot_flags(i, 0x41); - } - - /* Detect Master Key revision. */ - mkey_detect_revision(); - - /* Derive new device keys. */ - { - const uint32_t target_fw = exosphere_get_target_firmware(); - if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY); - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - derive_new_device_keys(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY); - } else { - /* No new keys to derive */ - } - } - - se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); - - /* Generate random data, transform with device key to get RNG key. */ - se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_RNGKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - set_aes_keyslot_flags(KEYSLOT_SWITCH_RNGKEY, 0xFF); - - /* Repeat for Session key. */ - se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_SESSIONKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - set_aes_keyslot_flags(KEYSLOT_SWITCH_SESSIONKEY, 0xFF); - - /* Generate test vector for our keys. */ - se_generate_stored_vector(); -} - -static void setup_boot_config(void) { - /* Load boot config only if dev unit. */ - if (configitem_is_retail()) { - bootconfig_clear(); - } else { - void *bootconfig_ptr = NX_BOOTLOADER_BOOTCONFIG_POINTER; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - bootconfig_ptr = NX_BOOTLOADER_BOOTCONFIG_POINTER_6X; - } - flush_dcache_range((uint8_t *)bootconfig_ptr, (uint8_t *)bootconfig_ptr + sizeof(bootconfig_t)); - bootconfig_load_and_verify((bootconfig_t *)bootconfig_ptr); - } -} - -static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { - /* Derive package2 key. */ - static const uint8_t package2_key_source[0x10] = {0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}; - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size); - flush_dcache_range((uint8_t *)src, (uint8_t *)src + src_size); - unsigned int keyslot = mkey_get_keyslot(master_key_rev); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_PACKAGE2KEY, keyslot, package2_key_source, 0x10); - - /* Perform Encryption. */ - se_aes_ctr_crypt(KEYSLOT_SWITCH_PACKAGE2KEY, dst, dst_size, src, src_size, ctr, ctr_size); -} - -static void verify_header_signature(package2_header_t *header) { - const uint8_t *modulus; - - if (configitem_is_retail()) { - static const uint8_t package2_modulus_retail[0x100] = { - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - }; - modulus = package2_modulus_retail; - } else { - static const uint8_t package2_modulus_dev[0x100] = { - 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, - 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, - 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, - 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, - 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, - 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, - 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, - 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, - 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, - 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, - 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, - 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, - 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, - 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, - 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, - 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B - }; - modulus = package2_modulus_dev; - } - - /* This is normally only allowed on dev units, but we'll allow it anywhere. */ - bool is_unsigned = EXOSPHERE_LOOSEN_PACKAGE2_RESTRICTIONS_FOR_DEBUG || bootconfig_is_package2_unsigned(); - if (!is_unsigned && se_rsa2048_pss_verify(header->signature, 0x100, modulus, 0x100, header->encrypted_header, 0x100) == 0) { - panic(0xF0000001); /* Invalid PK21 signature. */ - } -} - -static uint32_t get_package2_size(package2_meta_t *metadata) { - return metadata->ctr_dwords[0] ^ metadata->ctr_dwords[2] ^ metadata->ctr_dwords[3]; -} - -static bool validate_package2_metadata(package2_meta_t *metadata) { - if (metadata->magic != MAGIC_PK21) { - return false; - } - - - /* Package2 size, version number is stored XORed in header CTR. */ - /* Nintendo, what the fuck? */ - uint32_t package_size = metadata->ctr_dwords[0] ^ metadata->ctr_dwords[2] ^ metadata->ctr_dwords[3]; - uint8_t header_version = (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF); - - /* Ensure package isn't too big or too small. */ - if (package_size <= sizeof(package2_header_t) || package_size > PACKAGE2_SIZE_MAX) { - return false; - } - - /* Validate that we're working with a header we know how to handle. */ - if (header_version > MASTERKEY_REVISION_MAX) { - return false; - } - - /* Require aligned entrypoint. */ - if (metadata->entrypoint & 3) { - return false; - } - - /* Validate section size sanity. */ - if (metadata->section_sizes[0] + metadata->section_sizes[1] + metadata->section_sizes[2] + sizeof(package2_header_t) != package_size) { - return false; - } - - bool entrypoint_found = false; - - /* Header has space for 4 sections, but only 3 are validated/potentially loaded on hardware. */ - size_t cur_section_offset = 0; - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - /* Validate section size alignment. */ - if (metadata->section_sizes[section] & 3) { - return false; - } - - /* Validate section does not overflow. */ - if (check_32bit_additive_overflow(metadata->section_offsets[section], metadata->section_sizes[section])) { - return false; - } - - /* Check for entrypoint presence. */ - uint32_t section_end = metadata->section_offsets[section] + metadata->section_sizes[section]; - if (metadata->section_offsets[section] <= metadata->entrypoint && metadata->entrypoint < section_end) { - entrypoint_found = true; - } - - /* Ensure no overlap with later sections. */ - if (metadata->section_sizes[section] != 0) { - for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { - if (metadata->section_sizes[later_section] == 0) { - continue; - } - uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; - if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { - return false; - } - } - } - - bool check_hash = EXOSPHERE_LOOSEN_PACKAGE2_RESTRICTIONS_FOR_DEBUG == 0; - /* Validate section hashes. */ - if (metadata->section_sizes[section]) { - void *section_data = (void *)((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(package2_header_t) + cur_section_offset); - uint8_t calculated_hash[0x20]; - flush_dcache_range((uint8_t *)section_data, (uint8_t *)section_data + metadata->section_sizes[section]); - se_calculate_sha256(calculated_hash, section_data, metadata->section_sizes[section]); - if (check_hash && memcmp(calculated_hash, metadata->section_hashes[section], sizeof(metadata->section_hashes[section])) != 0) { - return false; - } - cur_section_offset += metadata->section_sizes[section]; - } - - } - - /* Ensure that entrypoint is present in one of our sections. */ - if (!entrypoint_found) { - return false; - } - - /* Perform version checks. */ - /* We will be compatible with all package2s released before current, but not newer ones. */ - if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1000_CURRENT) { - return true; - } - - return false; -} - -/* Decrypts package2 header, and returns the master key revision required. */ -static uint32_t decrypt_and_validate_header(package2_header_t *header) { - package2_meta_t metadata; - - if (bootconfig_is_package2_plaintext() == 0) { - uint32_t mkey_rev; - - /* Try to decrypt for all possible master keys. */ - for (mkey_rev = 0; mkey_rev <= mkey_get_revision(); mkey_rev++) { - package2_crypt_ctr(mkey_rev, &metadata, sizeof(package2_meta_t), &header->metadata, sizeof(package2_meta_t), header->metadata.ctr, sizeof(header->metadata.ctr)); - /* Copy the ctr (which stores information) into the decrypted metadata. */ - memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr)); - /* See if this is the correct key. */ - if (validate_package2_metadata(&metadata)) { - header->metadata = metadata; - return mkey_rev; - } - } - - /* Ensure we successfully decrypted the header. */ - if (mkey_rev > mkey_get_revision()) { - panic(0xFAF00003); - } - } else if (!validate_package2_metadata(&header->metadata)) { - panic(0xFAF0003); - } - return 0; -} - -static void load_package2_sections(package2_meta_t *metadata, uint32_t master_key_rev) { - /* By default, copy data directly from where NX_BOOTLOADER puts it. */ - void *load_buf = NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS; - - /* Check whether any of our sections overlap this region. If they do, we must relocate and copy from elsewhere. */ - bool needs_relocation = false; - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; - uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; - if (overlaps(section_start, section_end, (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS), (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS) + PACKAGE2_SIZE_MAX)) { - needs_relocation = true; - } - } - if (needs_relocation) { - /* This code should *always* succeed in finding a carveout within seven loops, */ - /* due to the section size limit, and section number limit. */ - /* However, Nintendo tries panics after 8 loops if a safe section is not found. */ - /* This should never be the case, mathematically. */ - /* We will replicate this behavior. */ - bool found_safe_carveout = false; - uint64_t potential_base_start = DRAM_BASE_PHYSICAL; - uint64_t potential_base_end = potential_base_start + PACKAGE2_SIZE_MAX; - for (unsigned int i = 0; i < 8; i++) { - int is_safe = 1; - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; - uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; - if (overlaps(section_start, section_end, potential_base_start, potential_base_end)) { - is_safe = 0; - } - } - found_safe_carveout |= is_safe; - if (found_safe_carveout) { - break; - } - potential_base_start += PACKAGE2_SIZE_MAX; - potential_base_end += PACKAGE2_SIZE_MAX; - } - if (!found_safe_carveout) { - generic_panic(); - } - /* Relocate to new carveout. */ - memcpy((void *)potential_base_start, load_buf, PACKAGE2_SIZE_MAX); - memset(load_buf, 0, PACKAGE2_SIZE_MAX); - load_buf = (void *)potential_base_start; - } - - size_t cur_section_offset = 0; - /* Copy each section to its appropriate location, decrypting if necessary. */ - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - if (metadata->section_sizes[section] == 0) { - continue; - } - - void *dst_start = (void *)(DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]); - void *src_start = load_buf + sizeof(package2_header_t) + cur_section_offset; - size_t size = (size_t)metadata->section_sizes[section]; - - if (bootconfig_is_package2_plaintext() && size != 0) { - memcpy(dst_start, src_start, size); - } else if (size != 0) { - package2_crypt_ctr(master_key_rev, dst_start, size, src_start, size, metadata->section_ctrs[section], 0x10); - } - cur_section_offset += size; - } - - /* Clear the encrypted package2 from memory. */ - memset(load_buf, 0, PACKAGE2_SIZE_MAX); -} - -static void copy_warmboot_bin_to_dram() { - uint8_t *warmboot_src; - - { - const uint32_t target_fw = exosphere_get_target_firmware(); - if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { - warmboot_src = (uint8_t *)0x4003E000; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - warmboot_src = (uint8_t *)0x4003D800; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - warmboot_src = (uint8_t *)0x4003B000; - } else { - return; - } - } - - uint8_t *warmboot_dst = (uint8_t *)0x8000D000; - const size_t warmboot_size = 0x2000; - - /* Flush cache, to ensure warmboot is where we need it to be. */ - flush_dcache_range(warmboot_src, warmboot_src + warmboot_size); - __dsb_sy(); - - /* Copy warmboot. */ - for (size_t i = 0; i < warmboot_size; i += sizeof(uint32_t)) { - write32le(warmboot_dst, i, read32le(warmboot_src, i)); - } - - /* Flush cache, to ensure warmboot is where we need it to be. */ - flush_dcache_range(warmboot_dst, warmboot_dst + warmboot_size); - __dsb_sy(); -} - -static void sync_with_nx_bootloader(int state) { - while (MAILBOX_NX_BOOTLOADER_SETUP_STATE(exosphere_get_target_firmware()) < state) { - wait(100); - } -} - -static void identity_unmap_dram(void) { - uintptr_t *mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - - mmu_unmap_range(1, mmu_l1_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_DRAM), IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_DRAM)); - tlb_invalidate_all_inner_shareable(); -} - -uintptr_t get_pk2ldr_stack_address(void) { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_PK2LDR) + 0x2000; -} - -/* This function is called during coldboot init, and validates a package2. */ -/* This package2 is read into memory by a concurrent BPMP bootloader. */ -void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { - /* Load Exosphere-specific config. */ - exosphere_load_config(); - configitem_set_debugmode_override(exosphere_should_override_debugmode_user() != 0, exosphere_should_override_debugmode_priv() != 0); - if (exosphere_should_disable_usermode_exception_handlers() != 0) { - configitem_disable_usermode_exception_handlers(); - } - if (exosphere_should_enable_usermode_pmu_access()) { - configitem_enable_usermode_pmu_access(); - } - - /* Setup the Security Engine. */ - setup_se(); - - /* Perform initial PMC register writes, if relevant. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAKE_REG32(PMC_BASE + 0x054) = 0x8000D000; - MAKE_REG32(PMC_BASE + 0x0A0) &= 0xFFF3FFFF; - MAKE_REG32(PMC_BASE + 0x818) &= 0xFFFFFFFE; - MAKE_REG32(PMC_BASE + 0x334) |= 0x10; - - const uint32_t target_fw = exosphere_get_target_firmware(); - if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x105; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_9_1_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x18C; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_9_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x16B; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x14A; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x129; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x0A8; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x087; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x006; - } else if (target_fw >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAKE_REG32(PMC_BASE + 0x360) = 0x105; - } - } - - wait(1000); - - bootup_misc_mmio(); - - setup_current_core_state(); - - /* Save boot reason to global. */ - bootconfig_load_boot_reason((volatile boot_reason_t *)(MAILBOX_NX_BOOTLOADER_BOOT_REASON(exosphere_get_target_firmware()))); - - /* Initialize cache'd random bytes for kernel. */ - randomcache_init(); - - /* memclear the initial copy of Exosphere running in IRAM (relocated to TZRAM by earlier code). */ - /* memset((void *)reloc_list->reloc_base, 0, reloc_list->loaded_bin_size); */ - - /* Let NX Bootloader know that we're running. */ - MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(exosphere_get_target_firmware()) = 1; - - /* Wait for 1 second, to allow time for NX_BOOTLOADER to draw to the screen. This is useful for debugging. */ - /* wait(1000000); */ - - /* Synchronize with NX BOOTLOADER. */ - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG); - - /* Load Boot Config into global. */ - setup_boot_config(); - - /* Set sysctr0 registers based on bootconfig. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - uint64_t sysctr0_val = bootconfig_get_value_for_sysctr0(); - MAKE_SYSCTR0_REG(0x8) = (uint32_t)((sysctr0_val >> 0) & 0xFFFFFFFFULL); - MAKE_SYSCTR0_REG(0xC) = (uint32_t)((sysctr0_val >> 32) & 0xFFFFFFFFULL); - MAKE_SYSCTR0_REG(0x0) = 3; - } - - /* Synchronize with NX BOOTLOADER. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X); - copy_warmboot_bin_to_dram(); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - setup_dram_magic_numbers(); - } - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2_4X); - } else { - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2); - } - - /* Make PMC (2.x+), MC (4.x+) registers secure-only */ - secure_additional_devices(); - - /* Remove the identity mapping for iRAM-C+D & TZRAM */ - /* For our crt0 to work, this doesn't actually unmap TZRAM */ - identity_unmap_iram_cd_tzram(); - - /* Load header from NX_BOOTLOADER-initialized DRAM. */ - package2_header_t header; - flush_dcache_range((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, (uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(header)); - memcpy(&header, NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, sizeof(header)); - flush_dcache_range((uint8_t *)&header, (uint8_t *)&header + sizeof(header)); - - /* Perform signature checks. */ - /* Special exosphere patching enable: All-zeroes signature + decrypted header implies unsigned and decrypted package2. */ - if (header.signature[0] == 0 && memcmp(header.signature, header.signature + 1, sizeof(header.signature) - 1) == 0 && header.metadata.magic == MAGIC_PK21) { - bootconfig_set_package2_plaintext_and_unsigned(); - } - - verify_header_signature(&header); - - /* Decrypt header, get key revision required. */ - uint32_t package2_mkey_rev = decrypt_and_validate_header(&header); - - /* Copy hash, if necessary. */ - if (bootconfig_is_recovery_boot()) { - bootconfig_set_package2_hash_for_recovery(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, get_package2_size(&header.metadata)); - } - - /* Load Package2 Sections. */ - load_package2_sections(&header.metadata, package2_mkey_rev); - - /* Clean up cache. */ - flush_dcache_all(); - invalidate_icache_all(); /* non-broadcasting */ - - /* Set CORE0 entrypoint for Package2. */ - set_core_entrypoint_and_argument(0, DRAM_BASE_PHYSICAL + header.metadata.entrypoint, 0); - - /* Remove the DRAM identity mapping. */ - if (0) { - identity_unmap_dram(); - } - - /* Synchronize with NX BOOTLOADER. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED_4X); - setup_4x_mmio(); - } else { - sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED); - } - - /* Prepare the SMC API with version-dependent SMCs. */ - set_version_specific_smcs(); - - /* Update SCR_EL3 depending on value in Bootconfig. */ - set_extabt_serror_taken_to_el3(bootconfig_take_extabt_serror_to_el3()); -} diff --git a/exosphere/src/package2.h b/exosphere/src/package2.h deleted file mode 100644 index ab2d9d01e..000000000 --- a/exosphere/src/package2.h +++ /dev/null @@ -1,121 +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_PACKAGE2_H -#define EXOSPHERE_PACKAGE2_H - -/* This is code responsible for validating a package2. Actual file reading is done by bootloader. */ - -#include "utils.h" -#include "bootconfig.h" -#include "exocfg.h" -#include "memory_map.h" - -/* Physaddr 0x40002EF8 */ -static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_NXBOOTLOADER_MAILBOX) + ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (0x000ull) : (0xE00ull)); -} - -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (get_nx_bootloader_mailbox_base(targetfw)) - -#define MAILBOX_NX_SECMON_BOOT_TIME(targetfw) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0x08ull) - -#define MAILBOX_NX_BOOTLOADER_SETUP_STATE(targetfw) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0xF8ull) - -#define NX_BOOTLOADER_STATE_INIT 0 -#define NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG 1 - -#define NX_BOOTLOADER_STATE_LOADED_PACKAGE2 2 -#define NX_BOOTLOADER_STATE_FINISHED 3 - -#define NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X 2 -#define NX_BOOTLOADER_STATE_LOADED_PACKAGE2_4X 3 -#define NX_BOOTLOADER_STATE_FINISHED_4X 4 - -/* Physaddr 0x40002EFC */ -#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(targetfw) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0xFCULL) - -#define MAILBOX_NX_BOOTLOADER_BOOT_REASON(targetfw) (MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0x10ULL) - -#define NX_BOOTLOADER_BOOTCONFIG_POINTER ((void *)(0x4003D000ull)) -#define NX_BOOTLOADER_BOOTCONFIG_POINTER_6X ((void *)(0x4003F800ull)) - -#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull)) - -#define DRAM_BASE_PHYSICAL (0x80000000ull) - -#define MAGIC_PK21 (0x31324B50) -#define PACKAGE2_SIZE_MAX 0x7FC000 -#define PACKAGE2_SECTION_MAX 0x3 - -#define PACKAGE2_MINVER_THEORETICAL 0x0 -#define PACKAGE2_MAXVER_100 0x2 -#define PACKAGE2_MAXVER_200 0x3 -#define PACKAGE2_MAXVER_300 0x4 -#define PACKAGE2_MAXVER_302 0x5 -#define PACKAGE2_MAXVER_400_410 0x6 -#define PACKAGE2_MAXVER_500_510 0x7 -#define PACKAGE2_MAXVER_600_610 0x8 -#define PACKAGE2_MAXVER_620 0x9 -#define PACKAGE2_MAXVER_700_800 0xA -#define PACKAGE2_MAXVER_810 0xB -#define PACKAGE2_MAXVER_900 0xC -#define PACKAGE2_MAXVER_910_920 0xD -#define PACKAGE2_MAXVER_1000_CURRENT 0xE - -#define PACKAGE2_MINVER_100 0x3 -#define PACKAGE2_MINVER_200 0x4 -#define PACKAGE2_MINVER_300 0x5 -#define PACKAGE2_MINVER_302 0x6 -#define PACKAGE2_MINVER_400_410 0x7 -#define PACKAGE2_MINVER_500_510 0x8 -#define PACKAGE2_MINVER_600_610 0x9 -#define PACKAGE2_MINVER_620 0xA -#define PACKAGE2_MINVER_700_800 0xB -#define PACKAGE2_MINVER_810 0xC -#define PACKAGE2_MINVER_900 0xD -#define PACKAGE2_MINVER_910_920 0xE -#define PACKAGE2_MINVER_1000_CURRENT 0xF - -typedef struct { - union { - uint8_t ctr[0x10]; - uint32_t ctr_dwords[0x4]; - }; - uint8_t section_ctrs[4][0x10]; - uint32_t magic; - uint32_t entrypoint; - uint32_t _0x58; - uint8_t version_max; /* Must be > TZ value. */ - uint8_t version_min; /* Must be < TZ value. */ - uint16_t _0x5E; - uint32_t section_sizes[4]; - uint32_t section_offsets[4]; - uint8_t section_hashes[4][0x20]; -} package2_meta_t; - -typedef struct { - uint8_t signature[0x100]; - union { - package2_meta_t metadata; - uint8_t encrypted_header[0x100]; - }; - uint8_t data[]; -} package2_header_t; - -void load_package2(coldboot_crt0_reloc_list_t *reloc_list); - -#endif diff --git a/exosphere/src/panic_color.h b/exosphere/src/panic_color.h deleted file mode 100644 index 3dc0d5f79..000000000 --- a/exosphere/src/panic_color.h +++ /dev/null @@ -1,39 +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_PANIC_COLOR_H -#define EXOSPHERE_PANIC_COLOR_H - -#define COLOR_0 0x00F00003 -#define COLOR_1 0x0F000003 -#define COLOR_2 0xF0000003 -#define COLOR_3 0x0FF00003 -#define COLOR_4 0xF0F00003 -#define COLOR_5 0xFF000003 -#define COLOR_6 0xFFF00003 -#define COLOR_7 0xAAF00003 -#define COLOR_8 0xAFA00003 -#define COLOR_9 0xFAA00003 -#define COLOR_A 0x33300003 -#define COLOR_B 0x06F00003 -#define COLOR_C 0x14800003 -#define COLOR_D 0x00300003 -#define COLOR_E 0x03000003 -#define COLOR_F 0xB6000003 - -#define PANIC_REBOOT 0x20 - -#endif \ No newline at end of file diff --git a/exosphere/src/pinmux.h b/exosphere/src/pinmux.h deleted file mode 100644 index f5c402204..000000000 --- a/exosphere/src/pinmux.h +++ /dev/null @@ -1,214 +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_PINMUX_H -#define EXOSPHERE_PINMUX_H - -#include <stdint.h> -#include "memory_map.h" - -#define PINMUX_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MISC) + 0x3000) -#define MAKE_PINMUX_REG(n) MAKE_REG32(PINMUX_BASE + n) - -#define PINMUX_TRISTATE (1 << 4) -#define PINMUX_PARKED (1 << 5) -#define PINMUX_INPUT (1 << 6) -#define PINMUX_PULL_NONE (0 << 2) -#define PINMUX_PULL_DOWN (1 << 2) -#define PINMUX_PULL_UP (2 << 2) -#define PINMUX_SELECT_FUNCTION0 0 -#define PINMUX_SELECT_FUNCTION1 1 -#define PINMUX_SELECT_FUNCTION2 2 -#define PINMUX_SELECT_FUNCTION3 3 -#define PINMUX_DRIVE_1X (0 << 13) -#define PINMUX_DRIVE_2X (1 << 13) -#define PINMUX_DRIVE_3X (2 << 13) -#define PINMUX_DRIVE_4X (3 << 13) - -typedef struct { - uint32_t sdmmc1_clk; - uint32_t sdmmc1_cmd; - uint32_t sdmmc1_dat3; - uint32_t sdmmc1_dat2; - uint32_t sdmmc1_dat1; - uint32_t sdmmc1_dat0; - uint32_t _r18; - uint32_t sdmmc3_clk; - uint32_t sdmmc3_cmd; - uint32_t sdmmc3_dat0; - uint32_t sdmmc3_dat1; - uint32_t sdmmc3_dat2; - uint32_t sdmmc3_dat3; - uint32_t _r34; - uint32_t pex_l0_rst_n; - uint32_t pex_l0_clkreq_n; - uint32_t pex_wake_n; - uint32_t pex_l1_rst_n; - uint32_t pex_l1_clkreq_n; - uint32_t sata_led_active; - uint32_t spi1_mosi; - uint32_t spi1_miso; - uint32_t spi1_sck; - uint32_t spi1_cs0; - uint32_t spi1_cs1; - uint32_t spi2_mosi; - uint32_t spi2_miso; - uint32_t spi2_sck; - uint32_t spi2_cs0; - uint32_t spi2_cs1; - uint32_t spi4_mosi; - uint32_t spi4_miso; - uint32_t spi4_sck; - uint32_t spi4_cs0; - uint32_t qspi_sck; - uint32_t qspi_cs_n; - uint32_t qspi_io0; - uint32_t qspi_io1; - uint32_t qspi_io2; - uint32_t qspi_io3; - uint32_t _ra0; - uint32_t dmic1_clk; - uint32_t dmic1_dat; - uint32_t dmic2_clk; - uint32_t dmic2_dat; - uint32_t dmic3_clk; - uint32_t dmic3_dat; - uint32_t gen1_i2c_scl; - uint32_t gen1_i2c_sda; - uint32_t gen2_i2c_scl; - uint32_t gen2_i2c_sda; - uint32_t gen3_i2c_scl; - uint32_t gen3_i2c_sda; - uint32_t cam_i2c_scl; - uint32_t cam_i2c_sda; - uint32_t pwr_i2c_scl; - uint32_t pwr_i2c_sda; - uint32_t uart1_tx; - uint32_t uart1_rx; - uint32_t uart1_rts; - uint32_t uart1_cts; - uint32_t uart2_tx; - uint32_t uart2_rx; - uint32_t uart2_rts; - uint32_t uart2_cts; - uint32_t uart3_tx; - uint32_t uart3_rx; - uint32_t uart3_rts; - uint32_t uart3_cts; - uint32_t uart4_tx; - uint32_t uart4_rx; - uint32_t uart4_rts; - uint32_t uart4_cts; - uint32_t dap1_fs; - uint32_t dap1_din; - uint32_t dap1_dout; - uint32_t dap1_sclk; - uint32_t dap2_fs; - uint32_t dap2_din; - uint32_t dap2_dout; - uint32_t dap2_sclk; - uint32_t dap4_fs; - uint32_t dap4_din; - uint32_t dap4_dout; - uint32_t dap4_sclk; - uint32_t cam1_mclk; - uint32_t cam2_mclk; - uint32_t jtag_rtck; - uint32_t clk_32k_in; - uint32_t clk_32k_out; - uint32_t batt_bcl; - uint32_t clk_req; - uint32_t cpu_pwr_req; - uint32_t pwr_int_n; - uint32_t shutdown; - uint32_t core_pwr_req; - uint32_t aud_mclk; - uint32_t dvfs_pwm; - uint32_t dvfs_clk; - uint32_t gpio_x1_aud; - uint32_t gpio_x3_aud; - uint32_t pcc7; - uint32_t hdmi_cec; - uint32_t hdmi_int_dp_hpd; - uint32_t spdif_out; - uint32_t spdif_in; - uint32_t usb_vbus_en0; - uint32_t usb_vbus_en1; - uint32_t dp_hpd0; - uint32_t wifi_en; - uint32_t wifi_rst; - uint32_t wifi_wake_ap; - uint32_t ap_wake_bt; - uint32_t bt_rst; - uint32_t bt_wake_ap; - uint32_t ap_wake_nfc; - uint32_t nfc_en; - uint32_t nfc_int; - uint32_t gps_en; - uint32_t gps_rst; - uint32_t cam_rst; - uint32_t cam_af_en; - uint32_t cam_flash_en; - uint32_t cam1_pwdn; - uint32_t cam2_pwdn; - uint32_t cam1_strobe; - uint32_t lcd_te; - uint32_t lcd_bl_pwm; - uint32_t lcd_bl_en; - uint32_t lcd_rst; - uint32_t lcd_gpio1; - uint32_t lcd_gpio2; - uint32_t ap_ready; - uint32_t touch_rst; - uint32_t touch_clk; - uint32_t modem_wake_ap; - uint32_t touch_int; - uint32_t motion_int; - uint32_t als_prox_int; - uint32_t temp_alert; - uint32_t button_power_on; - uint32_t button_vol_up; - uint32_t button_vol_down; - uint32_t button_slide_sw; - uint32_t button_home; - uint32_t pa6; - uint32_t pe6; - uint32_t pe7; - uint32_t ph6; - uint32_t pk0; - uint32_t pk1; - uint32_t pk2; - uint32_t pk3; - uint32_t pk4; - uint32_t pk5; - uint32_t pk6; - uint32_t pk7; - uint32_t pl0; - uint32_t pl1; - uint32_t pz0; - uint32_t pz1; - uint32_t pz2; - uint32_t pz3; - uint32_t pz4; - uint32_t pz5; -} tegra_pinmux_t; - -static inline volatile tegra_pinmux_t *pinmux_get_regs(void) -{ - return (volatile tegra_pinmux_t *)PINMUX_BASE; -} - -#endif diff --git a/exosphere/src/pmc.h b/exosphere/src/pmc.h deleted file mode 100644 index 24c851534..000000000 --- a/exosphere/src/pmc.h +++ /dev/null @@ -1,65 +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_PMC_H -#define EXOSPHERE_PMC_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere register definitions for the Tegra X1 PMC. */ - -static inline uintptr_t get_pmc_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull; -} - -#define PMC_BASE (get_pmc_base()) - -#define APBDEV_PMC_DPD_ENABLE_0 MAKE_REG32(PMC_BASE + 0x24) -#define APBDEV_PMC_PWRGATE_TOGGLE_0 MAKE_REG32(PMC_BASE + 0x30) -#define APBDEV_PMC_PWRGATE_STATUS_0 MAKE_REG32(PMC_BASE + 0x38) -#define APBDEV_PMC_SCRATCH0_0 MAKE_REG32(PMC_BASE + 0x50) -#define APBDEV_PMC_CRYPTO_OP_0 MAKE_REG32(PMC_BASE + 0xF4) -#define APBDEV_PM_0 MAKE_REG32(PMC_BASE + 0x14) -#define APBDEV_PMC_WAKE2_STATUS_0 MAKE_REG32(PMC_BASE + 0x168) -#define APBDEV_PMC_CNTRL2_0 MAKE_REG32(PMC_BASE + 0x440) -#define APBDEV_PMC_FUSE_CTRL MAKE_REG32(PMC_BASE + 0x450) -#define APBDEV_PMC_SCRATCH43_0 MAKE_REG32(PMC_BASE + 0x22C) -#define APBDEV_PMC_SEC_DISABLE8_0 MAKE_REG32(PMC_BASE + 0x5C0) -#define APBDEV_PMC_SECURE_SCRATCH112_0 MAKE_REG32(PMC_BASE + 0xB18) -#define APBDEV_PMC_SECURE_SCRATCH113_0 MAKE_REG32(PMC_BASE + 0xB1C) -#define APBDEV_PMC_SECURE_SCRATCH114_0 MAKE_REG32(PMC_BASE + 0xB20) -#define APBDEV_PMC_SECURE_SCRATCH115_0 MAKE_REG32(PMC_BASE + 0xB24) -#define APBDEV_PMC_SCRATCH200_0 MAKE_REG32(PMC_BASE + 0x840) -#define APBDEV_PMC_SEC_DISABLE3_0 MAKE_REG32(PMC_BASE + 0x2D8) -#define APBDEV_PMC_SECURE_SCRATCH34_0 MAKE_REG32(PMC_BASE + 0x368) -#define APBDEV_PMC_SECURE_SCRATCH35_0 MAKE_REG32(PMC_BASE + 0x36C) -#define APBDEV_PMC_SECURE_SCRATCH16_0 MAKE_REG32(PMC_BASE + 0x320) -#define APBDEV_PMC_SECURE_SCRATCH51_0 MAKE_REG32(PMC_BASE + 0x3AC) -#define APBDEV_PMC_SECURE_SCRATCH55_0 MAKE_REG32(PMC_BASE + 0x3BC) -#define APBDEV_PMC_SECURE_SCRATCH74_0 MAKE_REG32(PMC_BASE + 0x408) -#define APBDEV_PMC_SECURE_SCRATCH75_0 MAKE_REG32(PMC_BASE + 0x40C) -#define APBDEV_PMC_SECURE_SCRATCH76_0 MAKE_REG32(PMC_BASE + 0x410) -#define APBDEV_PMC_SECURE_SCRATCH77_0 MAKE_REG32(PMC_BASE + 0x414) -#define APBDEV_PMC_SECURE_SCRATCH78_0 MAKE_REG32(PMC_BASE + 0x418) -#define APBDEV_PMC_SECURE_SCRATCH99_0 MAKE_REG32(PMC_BASE + 0xAE4) -#define APBDEV_PMC_SECURE_SCRATCH100_0 MAKE_REG32(PMC_BASE + 0xAE8) -#define APBDEV_PMC_SECURE_SCRATCH101_0 MAKE_REG32(PMC_BASE + 0xAEC) -#define APBDEV_PMC_SECURE_SCRATCH102_0 MAKE_REG32(PMC_BASE + 0xAF0) -#define APBDEV_PMC_SECURE_SCRATCH103_0 MAKE_REG32(PMC_BASE + 0xAF4) -#define APBDEV_PMC_SECURE_SCRATCH39_0 MAKE_REG32(PMC_BASE + 0x37C) - -#endif diff --git a/exosphere/src/preprocessor.h b/exosphere/src/preprocessor.h deleted file mode 100644 index 3710c8317..000000000 --- a/exosphere/src/preprocessor.h +++ /dev/null @@ -1,207 +0,0 @@ -/* TuxSH: I added INC/DEC_10 to INC/DEC_32; tuples */ - -#ifndef EXOSPHERE_PREPROCESSOR_H -#define EXOSPHERE_PREPROCESSOR_H - -/*============================================================================= - Copyright (c) 2015 Paul Fultz II - cloak.h - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -==============================================================================*/ - -/*#ifndef CLOAK_GUARD_H -#define CLOAK_GUARD_H*/ - -#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) -#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ - -#define COMPL(b) PRIMITIVE_CAT(COMPL_, b) -#define COMPL_0 1 -#define COMPL_1 0 - -#define BITAND(x) PRIMITIVE_CAT(BITAND_, x) -#define BITAND_0(y) 0 -#define BITAND_1(y) y - -#define INC(x) PRIMITIVE_CAT(INC_, x) -#define INC_0 1 -#define INC_1 2 -#define INC_2 3 -#define INC_3 4 -#define INC_4 5 -#define INC_5 6 -#define INC_6 7 -#define INC_7 8 -#define INC_8 9 -#define INC_9 10 -#define INC_10 11 -#define INC_11 12 -#define INC_12 13 -#define INC_13 14 -#define INC_14 15 -#define INC_15 16 -#define INC_16 17 -#define INC_17 18 -#define INC_18 19 -#define INC_19 20 -#define INC_20 21 -#define INC_21 22 -#define INC_22 23 -#define INC_23 24 -#define INC_24 25 -#define INC_25 26 -#define INC_26 27 -#define INC_27 28 -#define INC_28 29 -#define INC_29 30 -#define INC_30 31 -#define INC_31 32 -#define INC_32 32 -#define INC_33 33 - -#define DEC(x) PRIMITIVE_CAT(DEC_, x) -#define DEC_0 0 -#define DEC_1 0 -#define DEC_2 1 -#define DEC_3 2 -#define DEC_4 3 -#define DEC_5 4 -#define DEC_6 5 -#define DEC_7 6 -#define DEC_8 7 -#define DEC_9 8 -#define DEC_10 9 -#define DEC_11 10 -#define DEC_12 11 -#define DEC_13 12 -#define DEC_14 13 -#define DEC_15 14 -#define DEC_16 15 -#define DEC_17 16 -#define DEC_18 17 -#define DEC_19 18 -#define DEC_20 19 -#define DEC_21 20 -#define DEC_22 21 -#define DEC_23 22 -#define DEC_24 23 -#define DEC_25 24 -#define DEC_26 25 -#define DEC_27 26 -#define DEC_28 27 -#define DEC_29 28 -#define DEC_30 29 -#define DEC_31 30 -#define DEC_32 31 -#define DEC_33 32 - -#define CHECK_N(x, n, ...) n -#define CHECK(...) CHECK_N(__VA_ARGS__, 0,) -#define PROBE(x) x, 1, - -#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x) -#define IS_PAREN_PROBE(...) PROBE(~) - -#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x)) -#define NOT_0 PROBE(~) - -#define COMPL(b) PRIMITIVE_CAT(COMPL_, b) -#define COMPL_0 1 -#define COMPL_1 0 - -#define BOOL(x) COMPL(NOT(x)) - -#define IIF(c) PRIMITIVE_CAT(IIF_, c) -#define IIF_0(t, ...) __VA_ARGS__ -#define IIF_1(t, ...) t - -#define IF(c) IIF(BOOL(c)) - -#define EAT(...) -#define EXPAND(...) __VA_ARGS__ -#define WHEN(c) IF(c)(EXPAND, EAT) - -#define EMPTY() -#define DEFER(id) id EMPTY() -#define OBSTRUCT(id) id DEFER(EMPTY)() - -#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) -#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) -#define EVAL5(...) __VA_ARGS__ - -#define REPEAT(count, macro, ...) \ - WHEN(count) \ - ( \ - OBSTRUCT(REPEAT_INDIRECT) () \ - ( \ - DEC(count), macro, __VA_ARGS__ \ - ) \ - OBSTRUCT(macro) \ - ( \ - DEC(count), __VA_ARGS__ \ - ) \ - ) -#define REPEAT_INDIRECT() REPEAT - -#define WHILE(pred, op, ...) \ - IF(pred(__VA_ARGS__)) \ - ( \ - OBSTRUCT(WHILE_INDIRECT) () \ - ( \ - pred, op, op(__VA_ARGS__) \ - ), \ - __VA_ARGS__ \ - ) -#define WHILE_INDIRECT() WHILE - -#define PRIMITIVE_COMPARE(x, y) IS_PAREN \ -( \ - COMPARE_ ## x ( COMPARE_ ## y) (()) \ -) - -#define IS_COMPARABLE(x) IS_PAREN( CAT(COMPARE_, x) (()) ) - -#define NOT_EQUAL(x, y) \ -IIF(BITAND(IS_COMPARABLE(x))(IS_COMPARABLE(y)) ) \ -( \ - PRIMITIVE_COMPARE, \ - 1 EAT \ -)(x, y) - -#define EQUAL(x, y) COMPL(NOT_EQUAL(x, y)) - -#define COMMA() , - -#define COMMA_IF(n) IF(n)(COMMA, EAT)() - -#define PLUS() + - -#define _TUPLE_ELEM_0(a, ...) a -#define _TUPLE_ELEM_1(a, b, ...) b -#define _TUPLE_ELEM_2(a, b, c, ...) c -#define _TUPLE_ELEM_3(a, b, c, d, ...) d -#define _TUPLE_ELEM_4(a, b, c, d, e, ...) e - -#define TUPLE_ELEM_0(T) EVAL(_TUPLE_ELEM_0 T) -#define TUPLE_ELEM_1(T) EVAL(_TUPLE_ELEM_1 T) -#define TUPLE_ELEM_2(T) EVAL(_TUPLE_ELEM_2 T) -#define TUPLE_ELEM_3(T) EVAL(_TUPLE_ELEM_3 T) -#define TUPLE_ELEM_4(T) EVAL(_TUPLE_ELEM_4 T) - -#define _TUPLE_FOLD_LEFT_0(i, T, op) (_TUPLE_ELEM_0 CAT(T,i)) op() -#define _TUPLE_FOLD_LEFT_1(i, T, op) (_TUPLE_ELEM_1 CAT(T,i)) op() -#define _TUPLE_FOLD_LEFT_2(i, T, op) (_TUPLE_ELEM_2 CAT(T,i)) op() -#define _TUPLE_FOLD_LEFT_3(i, T, op) (_TUPLE_ELEM_3 CAT(T,i)) op() -#define _TUPLE_FOLD_LEFT_4(i, T, op) (_TUPLE_ELEM_4 CAT(T,i)) op() - -#define TUPLE_FOLD_LEFT_0(len, T, op) EVAL(REPEAT(len, _TUPLE_FOLD_LEFT_0, T, op)) -#define TUPLE_FOLD_LEFT_1(len, T, op) EVAL(REPEAT(len, _TUPLE_FOLD_LEFT_1, T, op)) -#define TUPLE_FOLD_LEFT_2(len, T, op) EVAL(REPEAT(len, _TUPLE_FOLD_LEFT_2, T, op)) -#define TUPLE_FOLD_LEFT_3(len, T, op) EVAL(REPEAT(len, _TUPLE_FOLD_LEFT_3, T, op)) -#define TUPLE_FOLD_LEFT_4(len, T, op) EVAL(REPEAT(len, _TUPLE_FOLD_LEFT_4, T, op)) - -#endif diff --git a/exosphere/src/randomcache.c b/exosphere/src/randomcache.c deleted file mode 100644 index 08e9f475c..000000000 --- a/exosphere/src/randomcache.c +++ /dev/null @@ -1,91 +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 <string.h> - -#include "utils.h" -#include "randomcache.h" -#include "se.h" -#include "arm.h" - -/* TrustZone maintains a cache of random for the kernel. */ -/* So that requests can still be serviced even when a */ -/* usermode SMC is in progress. */ - -static uint8_t g_random_cache[0x400]; -static unsigned int g_random_cache_low = 0; -static unsigned int g_random_cache_high = 0x3FF; - - -void randomcache_refill_segment(unsigned int offset, unsigned int size) { - if (offset + size >= 0x400) { - size = 0x400 - offset; - } - - flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); - se_generate_random(KEYSLOT_SWITCH_RNGKEY, &g_random_cache[offset], size); - flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); - -} - -void randomcache_init(void) { - randomcache_refill_segment(0, 0x400); - g_random_cache_low = 0; - g_random_cache_high = 0x3FF; -} - -void randomcache_refill(void) { - unsigned int high_plus_one = (g_random_cache_high + 1) & 0x3FF; - if (g_random_cache_low != high_plus_one) { - /* Only refill if there's data to refill. */ - if (g_random_cache_low < high_plus_one) { - /* NOTE: There is a bug in official code that causes this to not work properly. */ - /* In particular, official code checks whether high_plus_one == 0x400. */ - /* However, because high_plus_one is &= 0x3FF'd, this can never be the case. */ - /* We will implement according to Nintendo's intention, and not include their bug. */ - /* This should have no impact on actual observable results, anyway, since this data is random anyway... */ - - if (g_random_cache_high != 0x3FF) { /* This is if (true) in Nintendo's code due to the above bug. */ - randomcache_refill_segment(high_plus_one, 0x400 - high_plus_one); - g_random_cache_high = (g_random_cache_high + 0x400 - high_plus_one) & 0x3FF; - } - - if (g_random_cache_low > 0) { - randomcache_refill_segment(0, g_random_cache_low); - g_random_cache_high = (g_random_cache_high + g_random_cache_low) & 0x3FF; - } - } else { /* g_random_cache_low > high_plus_one */ - randomcache_refill_segment(high_plus_one, g_random_cache_low - high_plus_one); - g_random_cache_high = g_random_cache_low - 1; - } - } -} - -void randomcache_getbytes(void *dst, size_t num_bytes) { - unsigned int low = g_random_cache_low; - if (num_bytes == 0) { - return; - } - memcpy(dst, &g_random_cache[low], num_bytes); - - unsigned int new_low = low + num_bytes; - if (new_low + 0x38 > 0x3FF) { - new_low = 0; - } - - g_random_cache_low = new_low; -} diff --git a/exosphere/src/randomcache.h b/exosphere/src/randomcache.h deleted file mode 100644 index a7ab89e5f..000000000 --- a/exosphere/src/randomcache.h +++ /dev/null @@ -1,29 +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_RANDOM_CACHE_H -#define EXOSPHERE_RANDOM_CACHE_H - -#include <stdint.h> - -/* This method must be called on startup. */ -void randomcache_init(void); -void randomcache_refill(void); - -void randomcache_getbytes(void *dst, size_t num_bytes); - - -#endif \ No newline at end of file diff --git a/exosphere/src/rsa_common.c b/exosphere/src/rsa_common.c deleted file mode 100644 index 2dfbb3c1b..000000000 --- a/exosphere/src/rsa_common.c +++ /dev/null @@ -1,20 +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 "rsa_common.h" - -/* Instantiate the shared RSA data inside a single translation unit. */ -rsa_shared_data_t g_rsa_shared_data = {}; \ No newline at end of file diff --git a/exosphere/src/rsa_common.h b/exosphere/src/rsa_common.h deleted file mode 100644 index 41be75c30..000000000 --- a/exosphere/src/rsa_common.h +++ /dev/null @@ -1,36 +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_RSA_COMMON_H -#define EXOSPHERE_RSA_COMMON_H -#include <stdint.h> - -typedef union { - struct { - uint8_t user_data[0x100]; - } storage_exp_mod; - struct { - uint32_t master_key_rev; - uint32_t type; - uint64_t expected_label_hash[4]; - } unwrap_titlekey; -} rsa_shared_data_t __attribute__((aligned(4))); - -_Static_assert(sizeof(rsa_shared_data_t) == 0x100); - -extern rsa_shared_data_t g_rsa_shared_data; - -#endif diff --git a/exosphere/src/sc7.c b/exosphere/src/sc7.c deleted file mode 100644 index 12e28a98c..000000000 --- a/exosphere/src/sc7.c +++ /dev/null @@ -1,305 +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 <stdbool.h> -#include <string.h> - -#include "utils.h" - -#include "car.h" -#include "bpmp.h" -#include "arm.h" -#include "configitem.h" -#include "cpu_context.h" -#include "flow.h" -#include "fuse.h" -#include "i2c.h" -#include "sc7.h" -#include "masterkey.h" -#include "pmc.h" -#include "se.h" -#include "smc_api.h" -#include "timers.h" -#include "misc.h" -#include "uart.h" -#include "exocfg.h" - -#define u8 uint8_t -#define u32 uint32_t -#include "sc7fw_bin.h" -#undef u8 -#undef u32 - -static void configure_battery_hiz_mode(void) { - clkrst_reboot(CARDEVICE_I2C1); - - if (configitem_is_hiz_mode_enabled() && !i2c_query_ti_charger_bit_7()) { - /* Configure HiZ mode. */ - i2c_set_ti_charger_bit_7(); - uint32_t start_time = get_time(); - bool should_wait = true; - /* TODO: This is GPIO-6 GPIO_IN_1 */ - while (MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_GPIO) + 0x634) & 1) { - if (get_time() - start_time > 50000) { - should_wait = false; - break; - } - } - if (should_wait) { - wait(0x100); - } - } - clkrst_disable(CARDEVICE_I2C1); -} - -static void enable_lp0_wake_events(void) { - wait(75); - APBDEV_PMC_CNTRL2_0 |= 0x200; /* Set WAKE_DET_EN. */ - wait(75); - APBDEV_PM_0 = 0xFFFFFFFF; /* Set all wake events. */ - APBDEV_PMC_WAKE2_STATUS_0 = 0xFFFFFFFF; /* Set all wake events. */ - wait(75); -} - -static void notify_pmic_shutdown(void) { - clkrst_reboot(CARDEVICE_I2C5); - i2c_init(I2C_5); - if (fuse_get_bootrom_patch_version() >= 0x7F) { - i2c_send_pmic_cpu_shutdown_cmd(); - } -} - -static void mitigate_jamais_vu(void) { - /* Jamais Vu mitigation #1: Ensure all other cores are off. */ - if (APBDEV_PMC_PWRGATE_STATUS_0 & 0xE00) { - generic_panic(); - } - - /* For debugging, make this check always pass. */ - if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0 || (get_debug_authentication_status() & 3) == 3)) { - FLOW_CTLR_HALT_COP_EVENTS_0 = 0x50000000; - } else { - FLOW_CTLR_HALT_COP_EVENTS_0 = 0x40000000; - } - - /* Jamais Vu mitigation #2: Ensure the BPMP is halted. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0 || (get_debug_authentication_status() & 3) == 3) { - /* BPMP should just be plainly halted, in debugging conditions. */ - if (FLOW_CTLR_HALT_COP_EVENTS_0 != 0x50000000) { - generic_panic(); - } - } else { - /* BPMP must be in never-woken-up halt mode, under normal conditions. */ - if (FLOW_CTLR_HALT_COP_EVENTS_0 != 0x40000000) { - generic_panic(); - } - } - - /* Jamais Vu mitigation #3: Ensure all relevant DMA controllers are held in reset. */ - if ((CLK_RST_CONTROLLER_RST_DEVICES_H_0 & 0x4000006) != 0x4000006) { - generic_panic(); - } -} - -static void configure_pmc_for_deep_powerdown(void) { - APBDEV_PMC_SCRATCH0_0 = 1; - APBDEV_PMC_DPD_ENABLE_0 |= 2; -} - -static void setup_bpmp_sc7_firmware(void) { - /* Mark PMC registers as not secure-world only, so BPMP can access them. */ - APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 &= 0xFFFFDFFF; - - /* Setup BPMP vectors. */ - BPMP_VECTOR_RESET = 0x40003000; /* lp0_entry_firmware_crt0 */ - BPMP_VECTOR_UNDEF = 0x40003004; /* Reboot. */ - BPMP_VECTOR_SWI = 0x40003004; /* Reboot. */ - BPMP_VECTOR_PREFETCH_ABORT = 0x40003004; /* Reboot. */ - BPMP_VECTOR_DATA_ABORT = 0x40003004; /* Reboot. */ - BPMP_VECTOR_UNK = 0x40003004; /* Reboot. */ - BPMP_VECTOR_IRQ = 0x40003004; /* Reboot. */ - BPMP_VECTOR_FIQ = 0x40003004; /* Reboot. */ - - /* Hold the BPMP in reset. */ - MAKE_CAR_REG(0x300) = 2; - - /* Copy BPMP firmware. */ - uint8_t *lp0_entry_code = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE)); - for (unsigned int i = 0; i < sc7fw_bin_size; i += 4) { - write32le(lp0_entry_code, i, read32le(sc7fw_bin, i)); - } - - flush_dcache_range(lp0_entry_code, lp0_entry_code + sc7fw_bin_size); - - /* Take the BPMP out of reset. */ - MAKE_CAR_REG(0x304) = 2; - - /* Start executing BPMP firmware. */ - FLOW_CTLR_HALT_COP_EVENTS_0 = 0; -} - -static void configure_flow_regs_for_sleep(void) { - unsigned int current_core = get_core_id(); - flow_set_cc4_ctrl(current_core, 0); - flow_set_halt_events(current_core, false); - FLOW_CTLR_L2FLUSH_CONTROL_0 = 0; - flow_set_csr(current_core, 2); -} - -static void save_tzram_state(void) { - /* TODO: Remove set suspend call once exo warmboots fully */ - set_suspend_for_debug(); - uint32_t tzram_cmac[0x4] = {0}; - - uint8_t *tzram_encryption_dst = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_ENCRYPTED_TZRAM)); - uint8_t *tzram_encryption_src = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM)); - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - tzram_encryption_src += 0x2000ull; - } - uint8_t *tzram_store_address = (uint8_t *)(WARMBOOT_GET_RAM_SEGMENT_ADDRESS(WARMBOOT_RAM_SEGMENT_ID_TZRAM)); - clear_priv_smc_in_progress(); - - /* Flush cache. */ - flush_dcache_all(); - - /* Encrypt and save TZRAM into DRAM using a random aes-256 key. */ - se_generate_random_key(KEYSLOT_SWITCH_LP0TZRAMKEY, KEYSLOT_SWITCH_RNGKEY); - - flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); - flush_dcache_range(tzram_encryption_src, tzram_encryption_src + LP0_TZRAM_SAVE_SIZE); - - /* Use the all-zero cmac buffer as an IV. */ - se_aes_256_cbc_encrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_encryption_dst, LP0_TZRAM_SAVE_SIZE, tzram_encryption_src, LP0_TZRAM_SAVE_SIZE, tzram_cmac); - flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); - - /* Copy encrypted TZRAM from IRAM to DRAM. */ - for (unsigned int i = 0; i < LP0_TZRAM_SAVE_SIZE; i += 4) { - write32le(tzram_store_address, i, read32le(tzram_encryption_dst, i)); - } - - flush_dcache_range(tzram_store_address, tzram_store_address + LP0_TZRAM_SAVE_SIZE); - - /* Compute CMAC. */ - se_compute_aes_256_cmac(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_cmac, sizeof(tzram_cmac), tzram_encryption_src, LP0_TZRAM_SAVE_SIZE); - - /* Write CMAC, lock registers. */ - APBDEV_PMC_SECURE_SCRATCH112_0 = tzram_cmac[0]; - APBDEV_PMC_SECURE_SCRATCH113_0 = tzram_cmac[1]; - APBDEV_PMC_SECURE_SCRATCH114_0 = tzram_cmac[2]; - APBDEV_PMC_SECURE_SCRATCH115_0 = tzram_cmac[3]; - APBDEV_PMC_SEC_DISABLE8_0 = 0x550000; - - /* Perform pre-2.0.0 PMC writes. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - /* TODO: Give these writes appropriate defines in pmc.h */ - - /* Save Encrypted context location + lock scratch register. */ - MAKE_REG32(PMC_BASE + 0x360) = WARMBOOT_GET_RAM_SEGMENT_PA(WARMBOOT_RAM_SEGMENT_ID_TZRAM); - MAKE_REG32(PMC_BASE + 0x2D8) = 0x10000; - - /* Save Encryption parameters (where to copy TZRAM to, source, destination, size) */ - MAKE_REG32(PMC_BASE + 0x340) = LP0_ENTRY_GET_RAM_SEGMENT_PA(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM); - MAKE_REG32(PMC_BASE + 0x344) = 0; - MAKE_REG32(PMC_BASE + 0x348) = LP0_ENTRY_GET_RAM_SEGMENT_PA(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM); - MAKE_REG32(PMC_BASE + 0x34C) = 0; - MAKE_REG32(PMC_BASE + 0x350) = LP0_ENTRY_GET_RAM_SEGMENT_PA(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM); - MAKE_REG32(PMC_BASE + 0x354) = LP0_TZRAM_SAVE_SIZE; - - /* Lock scratch registers. */ - MAKE_REG32(PMC_BASE + 0x2D8) = 0x555; - } -} - -static void save_se_state(void) { - /* Save security engine state. */ - uint8_t *se_state_dst = (uint8_t *)(WARMBOOT_GET_RAM_SEGMENT_ADDRESS(WARMBOOT_RAM_SEGMENT_ID_SE_STATE)); - se_check_error_status_reg(); - se_set_in_context_save_mode(true); - se_save_context(KEYSLOT_SWITCH_SRKGENKEY, KEYSLOT_SWITCH_RNGKEY, se_state_dst); - flush_dcache_range(se_state_dst, se_state_dst + 0x840); - APBDEV_PMC_SCRATCH43_0 = (uint32_t)(WARMBOOT_GET_RAM_SEGMENT_PA(WARMBOOT_RAM_SEGMENT_ID_SE_STATE)); - se_set_in_context_save_mode(false); - se_check_error_status_reg(); -} - -/* Save security engine, and go to sleep. */ -void save_se_and_power_down_cpu(void) { - /* Save context for warmboot to restore. */ - save_tzram_state(); - save_se_state(); - - /* Patch the bootrom to disable warmboot signature checks. */ - MAKE_REG32(PMC_BASE + 0x118) = 0x2202E012; - MAKE_REG32(PMC_BASE + 0x11C) = 0x6001DC28; - - if (!configitem_is_retail()) { - uart_send(UART_A, "OYASUMI", 8); - } - - finalize_powerdown(); -} - -uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument) { - /* TODO: 6.0.0 introduces heavy deja vu mitigations. */ - /* Exosphere may want to implement these. */ - - /* Ensure SMC call is to enter deep sleep. */ - if ((power_state & 0x17FFF) != 0x1001B) { - return 0xFFFFFFFD; - } - - /* Perform I2C comms with TI charger if required. */ - configure_battery_hiz_mode(); - - /* Enable LP0 Wake Event Detection. */ - enable_lp0_wake_events(); - - /* Alert the PMC of an iminent shutdown. */ - notify_pmic_shutdown(); - - /* Validate that the shutdown has correct context. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - mitigate_jamais_vu(); - } - - /* Signal to bootrom the next reset should be a warmboot. */ - configure_pmc_for_deep_powerdown(); - - /* Ensure that BPMP SC7 firmware is active. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - setup_bpmp_sc7_firmware(); - } - - /* Prepare the current core for sleep. */ - configure_flow_regs_for_sleep(); - - /* Save core context. */ - set_core_entrypoint_and_argument(get_core_id(), entrypoint, argument); - save_current_core_context(); - set_current_core_inactive(); - - /* Ensure that other cores are already asleep. */ - if (!(APBDEV_PMC_PWRGATE_STATUS_0 & 0xE00)) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - call_with_stack_pointer(get_smc_core012_stack_address(), save_se_and_power_down_cpu); - } else { - save_se_and_power_down_cpu(); - } - } - - generic_panic(); -} diff --git a/exosphere/src/sc7.h b/exosphere/src/sc7.h deleted file mode 100644 index 055a4ac1b..000000000 --- a/exosphere/src/sc7.h +++ /dev/null @@ -1,28 +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_SC7_H -#define EXOSPHERE_SC7_H - -#include <stdint.h> - -/* Exosphere Deep Sleep Entry implementation. */ - -#define LP0_TZRAM_SAVE_SIZE 0xE000 - -uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument); - -#endif \ No newline at end of file diff --git a/exosphere/src/se.c b/exosphere/src/se.c deleted file mode 100644 index dcc52f4ea..000000000 --- a/exosphere/src/se.c +++ /dev/null @@ -1,912 +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 <string.h> - -#include "utils.h" -#include "synchronization.h" -#include "interrupt.h" -#include "se.h" -#include "memory_map.h" -#include "arm.h" -#include "se.h" - -void trigger_se_rsa_op(void *buf, size_t size); -void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size); - -/* Globals for driver. */ -static unsigned int (*g_se_callback)(void); - -static unsigned int g_se_modulus_sizes[KEYSLOT_RSA_MAX]; -static unsigned int g_se_exp_sizes[KEYSLOT_RSA_MAX]; - -static bool g_se_generated_vector = false; -static uint8_t g_se_stored_test_vector[0x10]; - -/* Initialize a SE linked list. */ -void ll_init(volatile se_ll_t *ll, void *buffer, size_t size) { - ll->num_entries = 0; /* 1 Entry. */ - - if (buffer != NULL) { - ll->addr_info.address = (uint32_t) get_physical_address(buffer); - ll->addr_info.size = (uint32_t) size; - } else { - ll->addr_info.address = 0; - ll->addr_info.size = 0; - } - - flush_dcache_range((uint8_t *)ll, (uint8_t *)ll + sizeof(*ll)); -} - -void set_security_engine_callback(unsigned int (*callback)(void)) { - /* Set the callback. */ - g_se_callback = callback; - - /* Enable SE Interrupt firing for async op. */ - se_get_regs()->SE_INT_ENABLE = 0x10; -} - -/* Fires on Security Engine operation completion. */ -void se_operation_completed(void) { - se_get_regs()->SE_INT_ENABLE = 0; - unsigned int (*callback)(void) = g_se_callback; - if (callback != NULL) { - g_se_callback = NULL; - callback(); - } -} - -void se_check_error_status_reg(void) { - if (se_get_regs()->SE_ERR_STATUS) { - generic_panic(); - } -} - -void se_check_for_error(void) { - volatile tegra_se_t *se = se_get_regs(); - if (se->SE_INT_STATUS & 0x10000 || se->SE_STATUS & 3 || se->SE_ERR_STATUS) { - generic_panic(); - } -} - -void se_trigger_interrupt(void) { - intr_set_pending(INTERRUPT_ID_USER_SECURITY_ENGINE); -} - -void se_verify_flags_cleared(void) { - if (se_get_regs()->SE_STATUS & 3) { - generic_panic(); - } -} - -void se_generate_test_vector(void *vector) { - /* TODO: Implement real test vector generation. */ - memset(vector, 0, 0x10); -} - -void se_validate_stored_vector(void) { - if (!g_se_generated_vector) { - generic_panic(); - } - - uint8_t calc_vector[0x10]; - se_generate_test_vector(calc_vector); - - /* Ensure nobody's messed with the security engine while we slept. */ - if (memcmp(calc_vector, g_se_stored_test_vector, 0x10) != 0) { - generic_panic(); - } -} - -void se_generate_stored_vector(void) { - if (g_se_generated_vector) { - generic_panic(); - } - - se_generate_test_vector(g_se_stored_test_vector); - g_se_generated_vector = true; -} - -/* Set the flags for an AES keyslot. */ -void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Misc flags. */ - if (flags & ~0x80) { - se->SE_CRYPTO_KEYTABLE_ACCESS[keyslot] = ~flags; - } - - /* Disable keyslot reads. */ - if (flags & 0x80) { - se->SE_CRYPTO_SECURITY_PERKEY &= ~(1 << keyslot); - } -} - -/* Set the flags for an RSA keyslot. */ -void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_RSA_MAX) { - generic_panic(); - } - - /* Misc flags. */ - if (flags & ~0x80) { - /* TODO: Why are flags assigned this way? */ - se->SE_RSA_KEYTABLE_ACCESS[keyslot] = (((flags >> 4) & 4) | (flags & 3)) ^ 7; - } - - /* Disable keyslot reads. */ - if (flags & 0x80) { - se->SE_RSA_SECURITY_PERKEY &= ~(1 << keyslot); - } -} - -void clear_aes_keyslot(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Zero out the whole keyslot and IV. */ - for (unsigned int i = 0; i < 0x10; i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | i; - se->SE_CRYPTO_KEYTABLE_DATA = 0; - } -} - -void clear_rsa_keyslot(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_RSA_MAX) { - generic_panic(); - } - - /* Zero out the whole keyslot. */ - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Modulus[i] */ - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | i | 0x40; - se->SE_RSA_KEYTABLE_DATA = 0; - } - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Expontent[i] */ - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | i; - se->SE_RSA_KEYTABLE_DATA = 0; - } -} - -void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || key_size > KEYSIZE_AES_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (key_size >> 2); i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | i; - se->SE_CRYPTO_KEYTABLE_DATA = read32le(key, 4 * i); - } -} - -void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_RSA_MAX || modulus_size > KEYSIZE_RSA_MAX || exp_size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (modulus_size >> 2); i++) { - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | 0x40 | i; - se->SE_RSA_KEYTABLE_DATA = read32be(modulus, (4 * (modulus_size >> 2)) - (4 * i) - 4); - } - - for (size_t i = 0; i < (exp_size >> 2); i++) { - se->SE_RSA_KEYTABLE_ADDR = (keyslot << 7) | i; - se->SE_RSA_KEYTABLE_DATA = read32be(exponent, (4 * (exp_size >> 2)) - (4 * i) - 4); - } - - g_se_modulus_sizes[keyslot] = modulus_size; - g_se_exp_sizes[keyslot] = exp_size; -} - -void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || iv_size > 0x10) { - generic_panic(); - } - - for (size_t i = 0; i < (iv_size >> 2); i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | 8 | i; - se->SE_CRYPTO_KEYTABLE_DATA = read32le(iv, 4 * i); - } -} - -void clear_aes_keyslot_iv(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (0x10 >> 2); i++) { - se->SE_CRYPTO_KEYTABLE_ADDR = (keyslot << 4) | 8 | i; - se->SE_CRYPTO_KEYTABLE_DATA = 0; - } -} - -void set_se_ctr(const void *ctr) { - for (unsigned int i = 0; i < 4; i++) { - se_get_regs()->SE_CRYPTO_LINEAR_CTR[i] = read32le(ctr, i * 4); - } -} - -void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSIZE_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { - generic_panic(); - } - - se->SE_CONFIG = (ALG_AES_DEC | DST_KEYTAB); - se->SE_CRYPTO_CONFIG = keyslot_src << 24; - se->SE_CRYPTO_LAST_BLOCK = 0; - se->SE_CRYPTO_KEYTABLE_DST = keyslot_dst << 8; - - flush_dcache_range(wrapped_key, (const uint8_t *)wrapped_key + wrapped_key_size); - trigger_se_blocking_op(OP_START, NULL, 0, wrapped_key, wrapped_key_size); -} - -void se_aes_crypt_insecure_internal(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, unsigned int crypt_config, bool encrypt, unsigned int (*callback)(void)) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - if (size == 0) { - return; - } - - /* Setup Config register. */ - if (encrypt) { - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY); - } else { - se->SE_CONFIG = (ALG_AES_DEC | DST_MEMORY); - } - - /* Setup Crypto register. */ - se->SE_CRYPTO_CONFIG = crypt_config | (keyslot << 24) | (encrypt << 8); - - /* Mark this encryption as insecure -- this makes the SE not a secure busmaster. */ - se->SE_CRYPTO_CONFIG |= 0x80000000; - - /* Appropriate number of blocks. */ - se->SE_CRYPTO_LAST_BLOCK = (size >> 4) - 1; - - /* Set the callback, for after the async operation. */ - set_security_engine_callback(callback); - - /* Setup Input/Output lists */ - se->SE_IN_LL_ADDR = in_ll_paddr; - se->SE_OUT_LL_ADDR = out_ll_paddr; - - /* Set registers for operation. */ - se->SE_ERR_STATUS = se->SE_ERR_STATUS; - se->SE_INT_STATUS = se->SE_INT_STATUS; - se->SE_OPERATION = 1; - (void)(se->SE_OPERATION); - - /* Ensure writes go through. */ - __dsb_ish(); -} - -void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)) { - /* Unknown what this write does, but official code writes it for CTR mode. */ - se_get_regs()->SE_SPARE = 1; - set_se_ctr(ctr); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x81E, true, callback); -} - -void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { - set_aes_keyslot_iv(keyslot, iv, 0x10); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x44, true, callback); -} - -void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { - set_aes_keyslot_iv(keyslot, iv, 0x10); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback); -} - -void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void)) { - volatile tegra_se_t *se = se_get_regs(); - uint8_t stack_buf[KEYSIZE_RSA_MAX]; - - if (keyslot >= KEYSLOT_RSA_MAX || size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - /* Endian swap the input. */ - for (size_t i = 0; i < size; i++) { - stack_buf[i] = *((const uint8_t *)buf + size - i - 1); - } - - se->SE_CONFIG = (ALG_RSA | DST_RSAREG); - se->SE_RSA_CONFIG = keyslot << 24; - se->SE_RSA_KEY_SIZE = (g_se_modulus_sizes[keyslot] >> 6) - 1; - se->SE_RSA_EXP_SIZE = g_se_exp_sizes[keyslot] >> 2; - - set_security_engine_callback(callback); - - flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); - trigger_se_rsa_op(stack_buf, size); - - while (!(se->SE_INT_STATUS & 2)) { /* Wait a while */ } -} - -void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - uint8_t stack_buf[KEYSIZE_RSA_MAX]; - - if (keyslot >= KEYSLOT_RSA_MAX || src_size > KEYSIZE_RSA_MAX || dst_size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - /* Endian swap the input. */ - for (size_t i = 0; i < src_size; i++) { - stack_buf[i] = *((uint8_t *)src + src_size - i - 1); - } - - se->SE_CONFIG = (ALG_RSA | DST_RSAREG); - se->SE_RSA_CONFIG = keyslot << 24; - se->SE_RSA_KEY_SIZE = (g_se_modulus_sizes[keyslot] >> 6) - 1; - se->SE_RSA_EXP_SIZE = g_se_exp_sizes[keyslot] >> 2; - - flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); - trigger_se_blocking_op(OP_START, NULL, 0, stack_buf, src_size); - se_get_exp_mod_output(dst, dst_size); -} - -void se_get_exp_mod_output(void *buf, size_t size) { - size_t num_dwords = (size >> 2); - if (num_dwords < 1) { - return; - } - - uint32_t *p_out = ((uint32_t *)buf) + num_dwords - 1; - uint32_t offset = 0; - - /* Copy endian swapped output. */ - while (num_dwords) { - *p_out = read32be(se_get_regs()->SE_RSA_OUTPUT, offset); - offset += 4; - p_out--; - num_dwords--; - } -} - -bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size) { - uint8_t message[RSA_2048_BYTES]; - uint8_t h_buf[0x24]; - - /* Hardcode RSA with keyslot 0. */ - const uint8_t public_exponent[4] = {0x00, 0x01, 0x00, 0x01}; - set_rsa_keyslot(0, modulus, modulus_size, public_exponent, sizeof(public_exponent)); - se_synchronous_exp_mod(0, message, sizeof(message), signature, signature_size); - - /* Validate sanity byte. */ - if (message[RSA_2048_BYTES - 1] != 0xBC) { - return false; - } - - /* Copy Salt into MGF1 Hash Buffer. */ - memset(h_buf, 0, sizeof(h_buf)); - memcpy(h_buf, message + RSA_2048_BYTES - 0x20 - 0x1, 0x20); - - /* Decrypt maskedDB (via inline MGF1). */ - uint8_t seed = 0; - uint8_t mgf1_buf[0x20]; - for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) { - h_buf[sizeof(h_buf) - 1] = seed++; - flush_dcache_range(h_buf, h_buf + sizeof(h_buf)); - se_calculate_sha256(mgf1_buf, h_buf, sizeof(h_buf)); - for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) { - message[i] ^= mgf1_buf[i - ofs]; - } - } - - /* Constant lmask for rsa-2048-pss. */ - message[0] &= 0x7F; - - /* Validate DB is of the form 0000...0001. */ - for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) { - if (message[i] != 0) { - return false; - } - } - if (message[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { - return false; - } - - /* Check hash correctness. */ - uint8_t validate_buf[8 + 0x20 + 0x20]; - uint8_t validate_hash[0x20]; - - memset(validate_buf, 0, sizeof(validate_buf)); - flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); - se_calculate_sha256(&validate_buf[8], data, data_size); - memcpy(&validate_buf[0x28], &message[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20); - flush_dcache_range(validate_buf, validate_buf + sizeof(validate_buf)); - se_calculate_sha256(validate_hash, validate_buf, sizeof(validate_buf)); - return memcmp(h_buf, validate_hash, 0x20) == 0; -} - - -void trigger_se_rsa_op(void *buf, size_t size) { - volatile tegra_se_t *se = se_get_regs(); - se_ll_t in_ll; - - ll_init(&in_ll, (void *)buf, size); - - /* Set the input LL. */ - se->SE_IN_LL_ADDR = (uint32_t) get_physical_address(&in_ll); - - /* Set registers for operation. */ - se->SE_ERR_STATUS = se->SE_ERR_STATUS; - se->SE_INT_STATUS = se->SE_INT_STATUS; - se->SE_OPERATION = 1; - (void)(se->SE_OPERATION); - - /* Ensure writes go through. */ - __dsb_ish(); -} - -void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - se_ll_t in_ll; - se_ll_t out_ll; - - ll_init(&in_ll, (void *)src, src_size); - ll_init(&out_ll, dst, dst_size); - - __dsb_sy(); - - /* Set the LLs. */ - se->SE_IN_LL_ADDR = (uint32_t) get_physical_address(&in_ll); - se->SE_OUT_LL_ADDR = (uint32_t) get_physical_address(&out_ll); - - /* Set registers for operation. */ - se->SE_ERR_STATUS = se->SE_ERR_STATUS; - se->SE_INT_STATUS = se->SE_INT_STATUS; - se->SE_OPERATION = op; - (void)(se->SE_OPERATION); - - __dsb_ish(); - - while (!(se->SE_INT_STATUS & 0x10)) { /* Wait a while */ } - - se_check_for_error(); -} - -/* Secure AES Functionality. */ -void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t block[0x10] = {0}; - - if (src_size > sizeof(block) || dst_size > sizeof(block)) { - generic_panic(); - } - - /* Load src data into block. */ - if (src_size != 0) { - memcpy(block, src, src_size); - } - flush_dcache_range(block, block + sizeof(block)); - - /* Trigger AES operation. */ - se_get_regs()->SE_CRYPTO_LAST_BLOCK = 0; - trigger_se_blocking_op(OP_START, block, sizeof(block), block, sizeof(block)); - - /* Copy output data into dst. */ - flush_dcache_range(block, block + sizeof(block)); - if (dst_size != 0) { - memcpy(dst, block, dst_size); - } -} - -void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || ctr_size != 0x10) { - generic_panic(); - } - - if (src_size) { - flush_dcache_range((uint8_t *)src, (uint8_t *)src + src_size); - } - if (dst_size) { - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size); - } - - unsigned int num_blocks = src_size >> 4; - - /* Unknown what this write does, but official code writes it for CTR mode. */ - se->SE_SPARE = 1; - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x91E; - set_se_ctr(ctr); - - /* Handle any aligned blocks. */ - size_t aligned_size = (size_t)num_blocks << 4; - if (aligned_size) { - se->SE_CRYPTO_LAST_BLOCK = num_blocks - 1; - trigger_se_blocking_op(OP_START, dst, dst_size, src, aligned_size); - } - - /* Handle final, unaligned block. */ - if (aligned_size < dst_size && aligned_size < src_size) { - size_t last_block_size = dst_size - aligned_size; - if (src_size < dst_size) { - last_block_size = src_size - aligned_size; - } - se_perform_aes_block_operation(dst + aligned_size, last_block_size, (uint8_t *)src + aligned_size, src_size - aligned_size); - } - - if (dst_size) { - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size); - } -} - -void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - /* Set configuration high (256-bit vs 128-bit) based on parameter. */ - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY) | (config_high << 16); - se->SE_CRYPTO_CONFIG = keyslot << 24 | 0x100; - flush_dcache_range((uint8_t *)src, (uint8_t *)src + 0x10); - se_perform_aes_block_operation(dst, 0x10, src, 0x10); - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + 0x10); - -} - -void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0); -} - -void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0x202); -} - - -void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - se->SE_CONFIG = (ALG_AES_DEC | DST_MEMORY); - se->SE_CRYPTO_CONFIG = keyslot << 24; - flush_dcache_range((uint8_t *)src, (uint8_t *)src + 0x10); - se_perform_aes_block_operation(dst, 0x10, src, 0x10); - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + 0x10); -} - -void shift_left_xor_rb(uint8_t *key) { - uint8_t prev_high_bit = 0; - for (unsigned int i = 0; i < 0x10; i++) { - uint8_t cur_byte = key[0xF - i]; - key[0xF - i] = (cur_byte << 1) | (prev_high_bit); - prev_high_bit = cur_byte >> 7; - } - if (prev_high_bit) { - key[0xF] ^= 0x87; - } -} - -void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - if (data_size) { - flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); - } - - /* Generate the derived key, to be XOR'd with final output block. */ - uint8_t derived_key[0x10] = {0}; - se_aes_ecb_encrypt_block(keyslot, derived_key, sizeof(derived_key), derived_key, sizeof(derived_key), config_high); - shift_left_xor_rb(derived_key); - if (data_size & 0xF) { - shift_left_xor_rb(derived_key); - } - - se->SE_CONFIG = (ALG_AES_ENC | DST_HASHREG) | (config_high << 16); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | (0x145); - clear_aes_keyslot_iv(keyslot); - - unsigned int num_blocks = (data_size + 0xF) >> 4; - /* Handle aligned blocks. */ - if (num_blocks > 1) { - se->SE_CRYPTO_LAST_BLOCK = num_blocks - 2; - trigger_se_blocking_op(OP_START, NULL, 0, data, data_size); - se->SE_CRYPTO_CONFIG |= 0x80; - } - - /* Create final block. */ - uint8_t last_block[0x10] = {0}; - if (data_size & 0xF) { - memcpy(last_block, data + (data_size & ~0xF), data_size & 0xF); - last_block[data_size & 0xF] = 0x80; /* Last block = data || 100...0 */ - } else if (data_size >= 0x10) { - memcpy(last_block, data + data_size - 0x10, 0x10); - } - - for (unsigned int i = 0; i < 0x10; i++) { - last_block[i] ^= derived_key[i]; - } - - /* Perform last operation. */ - se->SE_CRYPTO_LAST_BLOCK = 0; - flush_dcache_range(last_block, last_block + sizeof(last_block)); - trigger_se_blocking_op(OP_START, NULL, 0, last_block, sizeof(last_block)); - - /* Copy output CMAC. */ - for (unsigned int i = 0; i < (cmac_size >> 2); i++) { - ((uint32_t *)cmac)[i] = read32le(se->SE_HASH_RESULT, i << 2); - } -} - -void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { - se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0); -} -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { - se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0x202); -} - -void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *iv) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX || src_size < 0x10) { - generic_panic(); - } - - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY) | (0x202 << 16); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x144; - set_aes_keyslot_iv(keyslot, iv, 0x10); - se->SE_CRYPTO_LAST_BLOCK = (src_size >> 4) - 1; - trigger_se_blocking_op(OP_START, dst, dst_size, src, src_size); -} - -/* SHA256 Implementation. */ -void se_calculate_sha256(void *dst, const void *src, size_t src_size) { - volatile tegra_se_t *se = se_get_regs(); - - /* Setup config for SHA256, size = BITS(src_size) */ - se->SE_CONFIG = (ENCMODE_SHA256 | ALG_SHA | DST_HASHREG); - se->SE_SHA_CONFIG = 1; - se->SE_SHA_MSG_LENGTH[0] = (uint32_t)(src_size << 3); - se->SE_SHA_MSG_LENGTH[1] = 0; - se->SE_SHA_MSG_LENGTH[2] = 0; - se->SE_SHA_MSG_LENGTH[3] = 0; - se->SE_SHA_MSG_LEFT[0] = (uint32_t)(src_size << 3); - se->SE_SHA_MSG_LEFT[1] = 0; - se->SE_SHA_MSG_LEFT[2] = 0; - se->SE_SHA_MSG_LEFT[3] = 0; - - /* Trigger the operation. */ - trigger_se_blocking_op(OP_START, NULL, 0, src, src_size); - - /* Copy output hash. */ - for (unsigned int i = 0; i < (0x20 >> 2); i++) { - ((uint32_t *)dst)[i] = read32be(se->SE_HASH_RESULT, i << 2); - } -} - -/* RNG API */ -void se_initialize_rng(unsigned int keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* To initialize the RNG, we'll perform an RNG operation into an output buffer. */ - /* This will be discarded, when done. */ - uint8_t output_buf[0x10]; - - se->SE_RNG_SRC_CONFIG = 3; /* Entropy enable + Entropy lock enable */ - se->SE_RNG_RESEED_INTERVAL = 70001; - se->SE_CONFIG = (ALG_RNG | DST_MEMORY); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x108; - se->SE_RNG_CONFIG = 5; - se->SE_CRYPTO_LAST_BLOCK = 0; - trigger_se_blocking_op(OP_START, output_buf, 0x10, NULL, 0); -} - -void se_generate_random(unsigned int keyslot, void *dst, size_t size) { - volatile tegra_se_t *se = se_get_regs(); - - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - uint32_t num_blocks = size >> 4; - size_t aligned_size = num_blocks << 4; - se->SE_CONFIG = (ALG_RNG | DST_MEMORY); - se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x108; - se->SE_RNG_CONFIG = 4; - - if (num_blocks >= 1) { - se->SE_CRYPTO_LAST_BLOCK = num_blocks - 1; - trigger_se_blocking_op(OP_START, dst, aligned_size, NULL, 0); - } - if (size > aligned_size) { - se_perform_aes_block_operation(dst + aligned_size, size - aligned_size, NULL, 0); - } -} - -/* SE context save API. */ -void se_set_in_context_save_mode(bool is_context_save_mode) { - volatile tegra_se_t *se = se_get_regs(); - - uint32_t val = se->SE_SE_SECURITY; - if (is_context_save_mode) { - val |= 0x10000; - } else { - val &= 0xFFFEFFFF; - } - se->SE_SE_SECURITY = val; - /* Perform a useless read from flags reg. */ - (void)(se->SE_STATUS); -} - -void se_generate_random_key(unsigned int dst_keyslot, unsigned int rng_keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - if (dst_keyslot >= KEYSLOT_AES_MAX || rng_keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Setup Config. */ - se->SE_CONFIG = (ALG_RNG | DST_KEYTAB); - se->SE_CRYPTO_CONFIG = (rng_keyslot << 24) | 0x108; - se->SE_RNG_CONFIG = 4; - se->SE_CRYPTO_LAST_BLOCK = 0; - - /* Generate low part of key. */ - se->SE_CRYPTO_KEYTABLE_DST = (dst_keyslot << 8); - trigger_se_blocking_op(OP_START, NULL, 0, NULL, 0); - /* Generate high part of key. */ - se->SE_CRYPTO_KEYTABLE_DST = (dst_keyslot << 8) | 1; - trigger_se_blocking_op(OP_START, NULL, 0, NULL, 0); -} - -void se_generate_srk(unsigned int srkgen_keyslot) { - volatile tegra_se_t *se = se_get_regs(); - - se->SE_CONFIG = (ALG_RNG | DST_SRK); - se->SE_CRYPTO_CONFIG = (srkgen_keyslot << 24) | 0x108; - se->SE_RNG_CONFIG = 6; - se->SE_CRYPTO_LAST_BLOCK = 0; - trigger_se_blocking_op(OP_START, NULL, 0, NULL, 0); -} - -void se_encrypt_with_srk(void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t output[0x80]; - uint8_t *aligned_out = (uint8_t *)(((uintptr_t)output + 0x7F) & ~0x3F); - if (dst_size > 0x10) { - generic_panic(); - } - if (src_size) { - flush_dcache_range((uint8_t *)src, (uint8_t *)src + src_size); - } - if (dst_size) { - flush_dcache_range(aligned_out, aligned_out + 0x10); - trigger_se_blocking_op(OP_CTX_SAVE, aligned_out, dst_size, src, src_size); - flush_dcache_range(aligned_out, aligned_out + 0x10); - memcpy(dst, aligned_out, dst_size); - } else { - trigger_se_blocking_op(OP_CTX_SAVE, aligned_out, 0, src, src_size); - } -} - -void se_save_context(unsigned int srkgen_keyslot, unsigned int rng_keyslot, void *dst) { - volatile tegra_se_t *se = se_get_regs(); - uint8_t _work_buf[0x80]; - uint8_t *work_buf = (uint8_t *)(((uintptr_t)_work_buf + 0x7F) & ~0x3F); - - /* Generate the SRK (context save encryption key). */ - se_generate_random_key(srkgen_keyslot, rng_keyslot); - se_generate_srk(srkgen_keyslot); - - flush_dcache_range(work_buf, work_buf + 0x10); - se_generate_random(rng_keyslot, work_buf, 0x10); - flush_dcache_range(work_buf, work_buf + 0x10); - - /* Save random initial block. */ - se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY); - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_MEM); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst, 0x10, work_buf, 0x10); - - /* Save Sticky Bits. */ - for (unsigned int i = 0; i < 0x2; i++) { - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_STICKY_BITS) | (i << CTX_SAVE_STICKY_BIT_INDEX_SHIFT); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x10 + (i * 0x10), 0x10, NULL, 0); - } - - /* Save AES Key Table. */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_LOW_BITS); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x30 + (i * 0x20), 0x10, NULL, 0); - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_HIGH_BITS); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x40 + (i * 0x20), 0x10, NULL, 0); - } - - /* Save AES Original IVs. */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_ORIGINAL_IV); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x230 + (i * 0x10), 0x10, NULL, 0); - } - - /* Save AES Updated IVs */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_UPDATED_IV); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x330 + (i * 0x10), 0x10, NULL, 0); - } - - /* Save RSA Keytable. */ - uint8_t *rsa_ctx_out = (uint8_t *)dst + 0x430; - for (unsigned int rsa_key = 0; rsa_key < KEYSLOT_RSA_MAX; rsa_key++) { - for (unsigned int mod_exp = 0; mod_exp < 2; mod_exp++) { - for (unsigned int sub_block = 0; sub_block < 0x10; sub_block++) { - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_RSA) | ((2 * rsa_key + (1 - mod_exp)) << CTX_SAVE_RSA_KEY_INDEX_SHIFT) | (sub_block << CTX_SAVE_RSA_KEY_BLOCK_INDEX_SHIFT); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(rsa_ctx_out, 0x10, NULL, 0); - rsa_ctx_out += 0x10; - } - } - } - - /* Save "Known Pattern. " */ - static const uint8_t context_save_known_pattern[0x10] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_MEM); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(dst + 0x830, 0x10, context_save_known_pattern, 0x10); - - /* Save SRK into PMC registers. */ - se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_SRK); - se->SE_CRYPTO_LAST_BLOCK = 0; - se_encrypt_with_srk(work_buf, 0, NULL, 0); - se->SE_CONFIG = 0; - se_encrypt_with_srk(work_buf, 0, NULL, 0); -} diff --git a/exosphere/src/se.h b/exosphere/src/se.h deleted file mode 100644 index a429caef7..000000000 --- a/exosphere/src/se.h +++ /dev/null @@ -1,231 +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_SE_H -#define EXOSPHERE_SE_H - -#include <assert.h> -#include <stdbool.h> -#include <stdint.h> -#include <stddef.h> - -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 security engine. */ - -#define KEYSLOT_SWITCH_LP0TZRAMKEY 0x2 -#define KEYSLOT_SWITCH_SRKGENKEY 0x8 -#define KEYSLOT_SWITCH_PACKAGE2KEY 0x8 -#define KEYSLOT_SWITCH_TEMPKEY 0x9 -#define KEYSLOT_SWITCH_SESSIONKEY 0xA -#define KEYSLOT_SWITCH_RNGKEY 0xB -#define KEYSLOT_SWITCH_MASTERKEY 0xC -#define KEYSLOT_SWITCH_DEVICEKEY 0xD - -/* This keyslot was added in 4.0.0. */ -#define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD -#define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE -#define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF - -/* This keyslot was added in 5.0.0. */ -#define KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY 0xA - -/* This keyslot was added in 6.00. */ -#define KEYSLOT_SWITCH_6XTSECKEY 0xC -#define KEYSLOT_SWITCH_6XTSECROOTKEY 0xD -#define KEYSLOT_SWITCH_6XSBK 0xE - -#define KEYSLOT_AES_MAX 0x10 -#define KEYSLOT_RSA_MAX 0x2 - -#define KEYSIZE_AES_MAX 0x20 -#define KEYSIZE_RSA_MAX 0x100 - -#define ALG_SHIFT (12) -#define ALG_DEC_SHIFT (8) -#define ALG_NOP (0 << ALG_SHIFT) -#define ALG_AES_ENC (1 << ALG_SHIFT) -#define ALG_AES_DEC ((1 << ALG_DEC_SHIFT) | ALG_NOP) -#define ALG_RNG (2 << ALG_SHIFT) -#define ALG_SHA (3 << ALG_SHIFT) -#define ALG_RSA (4 << ALG_SHIFT) - -#define DST_SHIFT (2) -#define DST_MEMORY (0 << DST_SHIFT) -#define DST_HASHREG (1 << DST_SHIFT) -#define DST_KEYTAB (2 << DST_SHIFT) -#define DST_SRK (3 << DST_SHIFT) -#define DST_RSAREG (4 << DST_SHIFT) - -#define ENCMODE_SHIFT (24) -#define DECMODE_SHIFT (16) -#define ENCMODE_SHA256 (5 << ENCMODE_SHIFT) - -#define HASH_DISABLE (0x0) -#define HASH_ENABLE (0x1) - -#define OP_ABORT 0 -#define OP_START 1 -#define OP_RESTART 2 -#define OP_CTX_SAVE 3 -#define OP_RESTART_IN 4 - -#define CTX_SAVE_SRC_SHIFT 29 -#define CTX_SAVE_SRC_STICKY_BITS (0 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_KEYTABLE_AES (2 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_KEYTABLE_RSA (1 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_MEM (4 << CTX_SAVE_SRC_SHIFT) -#define CTX_SAVE_SRC_SRK (6 << CTX_SAVE_SRC_SHIFT) - -#define CTX_SAVE_KEY_LOW_BITS 0 -#define CTX_SAVE_KEY_HIGH_BITS 1 -#define CTX_SAVE_KEY_ORIGINAL_IV 2 -#define CTX_SAVE_KEY_UPDATED_IV 3 - -#define CTX_SAVE_STICKY_BIT_INDEX_SHIFT 24 -#define CTX_SAVE_KEY_INDEX_SHIFT 8 -#define CTX_SAVE_RSA_KEY_INDEX_SHIFT 16 -#define CTX_SAVE_RSA_KEY_BLOCK_INDEX_SHIFT 12 - -#define RSA_2048_BYTES 0x100 - -typedef struct { - uint32_t SE_SE_SECURITY; - uint32_t SE_TZRAM_SECURITY; - uint32_t SE_OPERATION; - uint32_t SE_INT_ENABLE; - uint32_t SE_INT_STATUS; - uint32_t SE_CONFIG; - uint32_t SE_IN_LL_ADDR; - uint32_t SE_IN_CUR_BYTE_ADDR; - uint32_t SE_IN_CUR_LL_ID; - uint32_t SE_OUT_LL_ADDR; - uint32_t SE_OUT_CUR_BYTE_ADDR; - uint32_t SE_OUT_CUR_LL_ID; - uint32_t SE_HASH_RESULT[0x10]; - uint32_t SE_CTX_SAVE_CONFIG; - uint32_t _0x74[0x63]; - uint32_t SE_SHA_CONFIG; - uint32_t SE_SHA_MSG_LENGTH[0x4]; - uint32_t SE_SHA_MSG_LEFT[0x4]; - uint32_t _0x224[0x17]; - uint32_t SE_CRYPTO_SECURITY_PERKEY; - uint32_t SE_CRYPTO_KEYTABLE_ACCESS[0x10]; - uint32_t _0x2C4[0x10]; - uint32_t SE_CRYPTO_CONFIG; - uint32_t SE_CRYPTO_LINEAR_CTR[0x4]; - uint32_t SE_CRYPTO_LAST_BLOCK; - uint32_t SE_CRYPTO_KEYTABLE_ADDR; - uint32_t SE_CRYPTO_KEYTABLE_DATA; - uint32_t _0x324[0x3]; - uint32_t SE_CRYPTO_KEYTABLE_DST; - uint32_t _0x334[0x3]; - uint32_t SE_RNG_CONFIG; - uint32_t SE_RNG_SRC_CONFIG; - uint32_t SE_RNG_RESEED_INTERVAL; - uint32_t _0x34C[0x2D]; - uint32_t SE_RSA_CONFIG; - uint32_t SE_RSA_KEY_SIZE; - uint32_t SE_RSA_EXP_SIZE; - uint32_t SE_RSA_SECURITY_PERKEY; - uint32_t SE_RSA_KEYTABLE_ACCESS[0x2]; - uint32_t _0x418[0x2]; - uint32_t SE_RSA_KEYTABLE_ADDR; - uint32_t SE_RSA_KEYTABLE_DATA; - uint32_t SE_RSA_OUTPUT[0x40]; - uint32_t _0x528[0xB6]; - uint32_t SE_STATUS; - uint32_t SE_ERR_STATUS; - uint32_t SE_MISC; - uint32_t SE_SPARE; - uint32_t SE_ENTROPY_DEBUG_COUNTER; - uint32_t _0x814; - uint32_t _0x818; - uint32_t _0x81C; - uint32_t _0x820[0x5F8]; -} tegra_se_t; - -typedef struct { - uint32_t address; - uint32_t size; -} se_addr_info_t; - -typedef struct { - uint32_t num_entries; /* Set to total entries - 1 */ - se_addr_info_t addr_info; /* This should really be an array...but for our use case it works. */ -} se_ll_t; - -static inline volatile tegra_se_t *se_get_regs(void) { - return (volatile tegra_se_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_SE)); -} - -/* This function MUST be registered to fire on the appropriate interrupt. */ -void se_operation_completed(void); - -void se_check_error_status_reg(void); -void se_check_for_error(void); -void se_trigger_interrupt(void); - -void se_validate_stored_vector(void); -void se_generate_stored_vector(void); - -void se_verify_flags_cleared(void); - -void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags); -void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags); -void clear_aes_keyslot(unsigned int keyslot); -void clear_rsa_keyslot(unsigned int keyslot); - -void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size); -void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size); -void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size); -void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size); -void set_se_ctr(const void *ctr); - -/* Insecure AES API */ -void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)); -void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); -void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); - -/* Secure AES API */ -void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); -void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size); -void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *iv); - -/* Hash API */ -void se_calculate_sha256(void *dst, const void *src, size_t src_size); - -/* RSA API */ -void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void)); -void se_get_exp_mod_output(void *buf, size_t size); -void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size); - -/* RNG API */ -void se_initialize_rng(unsigned int keyslot); -void se_generate_random(unsigned int keyslot, void *dst, size_t size); - -/* SE context save API. */ -void se_generate_srk(unsigned int srkgen_keyslot); -void se_set_in_context_save_mode(bool is_context_save_mode); -void se_generate_random_key(unsigned int dst_keyslot, unsigned int rng_keyslot); -void se_save_context(unsigned int srk_keyslot, unsigned int rng_keyslot, void *dst); - -#endif /* EXOSPHERE_SE_H */ diff --git a/exosphere/src/sealedkeys.c b/exosphere/src/sealedkeys.c deleted file mode 100644 index dd4e7f835..000000000 --- a/exosphere/src/sealedkeys.c +++ /dev/null @@ -1,91 +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 <string.h> - -#include "utils.h" -#include "exocfg.h" -#include "sealedkeys.h" -#include "se.h" - -static const uint8_t g_titlekey_seal_key_source[0x10] = { - 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 -}; - -static const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX_5X][0x10] = { - {0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6}, - {0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06}, - {0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63}, - {0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D}, - {0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE}, - {0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9}, - {0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B} -}; - -bool usecase_is_invalid(unsigned int usecase) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - return usecase >= CRYPTOUSECASE_MAX_5X; - } else { - return usecase >= CRYPTOUSECASE_MAX; - } -} - -void seal_key_internal(void *dst, const void *src, const uint8_t *seal_key_source) { - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); -} - -void unseal_key_internal(unsigned int keyslot, const void *src, const uint8_t *seal_key_source) { - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); - decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, src, 0x10); -} - - -void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size) { - if (dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - seal_key_internal(dst, src, g_titlekey_seal_key_source); - -} - -void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { - if (src_size != 0x10) { - generic_panic(); - } - - unseal_key_internal(keyslot, src, g_titlekey_seal_key_source); -} - - -void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase) { - if (usecase_is_invalid(usecase) || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - - seal_key_internal(dst, src, g_seal_key_sources[usecase]); -} - -void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase) { - if (usecase_is_invalid(usecase) || src_size != 0x10) { - generic_panic(); - } - - unseal_key_internal(keyslot, src, g_seal_key_sources[usecase]); -} \ No newline at end of file diff --git a/exosphere/src/sealedkeys.h b/exosphere/src/sealedkeys.h deleted file mode 100644 index 67e311620..000000000 --- a/exosphere/src/sealedkeys.h +++ /dev/null @@ -1,41 +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_SEALED_KEYS_H -#define EXOSPHERE_SEALED_KEYS_H - -#include <stdint.h> - -/* Key sealing/unsealing functionality. */ - -#define CRYPTOUSECASE_AES 0 -#define CRYPTOUSECASE_RSAPRIVATE 1 -#define CRYPTOUSECASE_SECUREEXPMOD 2 -#define CRYPTOUSECASE_RSAOAEP 3 -#define CRYPTOUSECASE_RSAIMPORT 4 -#define CRYPTOUSECASE_UNK5 5 -#define CRYPTOUSECASE_UNK6 6 - -#define CRYPTOUSECASE_MAX 4 -#define CRYPTOUSECASE_MAX_5X 7 - -void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size); -void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size); - -void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase); -void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase); - -#endif \ No newline at end of file diff --git a/exosphere/src/smc_ams.c b/exosphere/src/smc_ams.c deleted file mode 100644 index 235dda460..000000000 --- a/exosphere/src/smc_ams.c +++ /dev/null @@ -1,290 +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 <stdatomic.h> -#include <stdbool.h> -#include <stdint.h> -#include <string.h> - -#include "utils.h" -#include "smc_ams.h" -#include "arm.h" -#include "synchronization.h" -#include "memory_map.h" -#include "mmu.h" -#include "userpage.h" -#include "exocfg.h" - -static atomic_flag g_ams_userpage_mapped = ATOMIC_FLAG_INIT; -static atomic_flag g_ams_iram_page_mapped = ATOMIC_FLAG_INIT; - -static inline uintptr_t get_ams_user_page_secure_monitor_addr(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_USER_PAGE); -} - -static inline uintptr_t get_ams_iram_page_secure_monitor_addr(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE); -} - -#define AMS_USER_PAGE_SECURE_MONITOR_ADDR (get_ams_user_page_secure_monitor_addr()) -#define AMS_IRAM_PAGE_SECURE_MONITOR_ADDR (get_ams_iram_page_secure_monitor_addr()) - - -static inline uintptr_t get_page_for_address(void *address) { - return ((uintptr_t)(address)) & ~0xFFFULL; -} - -static bool ams_is_user_addr_valid(uintptr_t user_address) { - /* Check that the address is in dram. */ - uintptr_t page_address = get_page_for_address((void *)user_address); - return (page_address - 0x80000000ull) < (6ull << 30); -} - -static bool ams_is_iram_addr_valid(uintptr_t iram_address) { - /* Check that the address is in iram. */ - return 0x40000000ULL <= iram_address && iram_address <= 0x4003FFFFULL; -} - -static void ams_map_userpage(uintptr_t user_address) { - lock_acquire(&g_ams_userpage_mapped); - static const uint64_t userpage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_NORMAL; - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - mmu_map_page(mmu_l3_tbl, AMS_USER_PAGE_SECURE_MONITOR_ADDR, get_page_for_address((void *)user_address), userpage_attributes); - tlb_invalidate_page_inner_shareable((void *)AMS_USER_PAGE_SECURE_MONITOR_ADDR); -} - -static void ams_unmap_userpage(void) { - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - mmu_unmap_page(mmu_l3_tbl, AMS_USER_PAGE_SECURE_MONITOR_ADDR); - tlb_invalidate_page_inner_shareable((void *)AMS_USER_PAGE_SECURE_MONITOR_ADDR); - lock_release(&g_ams_userpage_mapped); -} - -void ams_map_irampage(uintptr_t iram_address) { - lock_acquire(&g_ams_iram_page_mapped); - static const uint64_t irampage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_DEVICE; - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - mmu_map_page(mmu_l3_tbl, AMS_IRAM_PAGE_SECURE_MONITOR_ADDR, get_page_for_address((void *)iram_address), irampage_attributes); - tlb_invalidate_page_inner_shareable((void *)AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); -} - -void ams_unmap_irampage(void) { - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - mmu_unmap_page(mmu_l3_tbl, AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); - tlb_invalidate_page_inner_shareable((void *)AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); - lock_release(&g_ams_iram_page_mapped); -} - -uint32_t ams_iram_copy(smc_args_t *args) { - /* Implements a DRAM <-> IRAM copy of up to one page. */ - /* This operation is necessary to implement reboot-to-payload. */ - /* 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. */ - - const uintptr_t dram_address = (uintptr_t)args->X[1]; - const uintptr_t iram_address = (uintptr_t)args->X[2]; - const uintptr_t dram_page_offset = (dram_address & 0xFFFULL); - const uintptr_t iram_page_offset = (iram_address & 0xFFFULL); - const size_t size = args->X[3]; - const uint32_t option = (uint32_t)args->X[4]; - - /* Validate addresses. */ - if (!ams_is_user_addr_valid(dram_address) || !ams_is_iram_addr_valid(iram_address)) { - return 2; - } - - /* Validate size. */ - if (size > 0x1000 || (size + dram_page_offset) > 0x1000 || (size + iram_page_offset) > 0x1000) { - return 2; - } - - /* Validate alignment. */ - if (size % sizeof(uint32_t) || dram_page_offset % sizeof(uint32_t) || iram_page_offset % sizeof(uint32_t)) { - return 2; - } - - /* Validate argument. */ - if (option != 0 && option != 1) { - return 2; - } - - /* Map pages. */ - ams_map_userpage(dram_address); - ams_map_irampage(iram_address); - - /* Set source/destination for copy. */ - volatile uint32_t *dram_ptr = (volatile uint32_t *)(AMS_USER_PAGE_SECURE_MONITOR_ADDR + dram_page_offset); - volatile uint32_t *iram_ptr = (volatile uint32_t *)(AMS_IRAM_PAGE_SECURE_MONITOR_ADDR + iram_page_offset); - - volatile uint32_t *dst; - volatile uint32_t *src; - const size_t num_dwords = size / sizeof(uint32_t); - if (option == 0) { - dst = dram_ptr; - src = iram_ptr; - } else { - dst = iram_ptr; - src = dram_ptr; - } - - /* Actually copy data. */ - for (size_t i = 0; i < num_dwords; i++) { - dst[i] = src[i]; - } - - /* Flush! */ - flush_dcache_range((void *)dst, (void *)(dst + num_dwords)); - - /* Unmap pages. */ - ams_unmap_irampage(); - ams_unmap_userpage(); - - return 0; -} - -uint32_t ams_write_address(smc_args_t *args) { - /* Implements a write to a DRAM page. */ - /* This operation can be used to write to read-only pages. */ - /* 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). */ - - const uintptr_t el0_virtual_address = (uintptr_t)args->X[1]; - const uintptr_t dram_address = get_physical_address_el0(el0_virtual_address); - const uintptr_t dram_page_offset = (dram_address & 0xFFFULL); - const size_t size = (size_t)(args->X[3]); - - /* Validate addresses. */ - if (!ams_is_user_addr_valid(dram_address)) { - return 2; - } - - /* Validate size. */ - switch (size) { - case 1: - case 2: - case 4: - case 8: - if ((size + dram_page_offset) > 0x1000) { - return 2; - } - break; - default: - return 2; - } - - /* Validate alignment. */ - if (dram_page_offset % size) { - return 2; - } - - /* Map pages. */ - ams_map_userpage(dram_address); - - /* Write data. */ - uintptr_t dram_ptr = (uintptr_t)(AMS_USER_PAGE_SECURE_MONITOR_ADDR + dram_page_offset); - switch (size) { - case 1: - *((volatile uint8_t *)dram_ptr) = (uint8_t)(args->X[2]); - break; - case 2: - *((volatile uint16_t *)dram_ptr) = (uint16_t)(args->X[2]); - break; - case 4: - *((volatile uint32_t *)dram_ptr) = (uint32_t)(args->X[2]); - break; - case 8: - *((volatile uint64_t *)dram_ptr) = args->X[2]; - break; - default: - generic_panic(); - } - - /* Flush! */ - flush_dcache_range((void *)dram_ptr, (void *)(dram_ptr + size)); - - /* Unmap pages. */ - ams_unmap_userpage(); - - return 0; -} - -uint32_t ams_get_emummc_config(smc_args_t *args) { - /* This retrieves configuration for the current emummc context. */ - /* 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. */ - const uint32_t mmc_id = (uint32_t)args->X[1]; - const uintptr_t dram_address = args->X[2]; - const uintptr_t dram_page_offset = (dram_address & 0xFFFULL); - const exo_emummc_config_t *emummc_cfg = exosphere_get_emummc_config(); - - if (mmc_id != EMUMMC_MMC_NAND) { - /* Emummc config for non-NAND storage is not yet implemented. */ - return 1; - } - - /* Require page alignment for input address. */ - if (!ams_is_user_addr_valid(dram_address) || dram_page_offset > 0x1000 - 0x100) { - return 2; - } - - /* Map pages. */ - ams_map_userpage(dram_address); - - void *user_address = (void *)(AMS_USER_PAGE_SECURE_MONITOR_ADDR + dram_page_offset); - - /* Copy redirection dir out to user. */ - memcpy((void *)((uintptr_t)user_address + sizeof(emummc_cfg->file_cfg)), emummc_cfg->emu_dir_path, sizeof(emummc_cfg->emu_dir_path)); - - if (emummc_cfg->base_cfg.type == EMUMMC_TYPE_NONE) { - /* Just copy base config. */ - memset(args, 0, sizeof(*args)); - memcpy(&args->X[1], emummc_cfg, sizeof(emummc_cfg->base_cfg)); - _Static_assert(sizeof(emummc_cfg->base_cfg) <= sizeof(*args) - sizeof(args->X[0]), "Emunand base config too big!"); - - /* Unmap pages. */ - ams_unmap_userpage(); - return 0; - } else if (emummc_cfg->base_cfg.type == EMUMMC_TYPE_PARTITION) { - /* Copy base config and partition config. */ - memset(args, 0, sizeof(*args)); - memcpy(&args->X[1], emummc_cfg, sizeof(emummc_cfg->base_cfg) + sizeof(emummc_cfg->partition_cfg)); - _Static_assert(sizeof(emummc_cfg->base_cfg) + sizeof(emummc_cfg->partition_cfg) <= sizeof(*args) - sizeof(args->X[0]), "Emunand partition config too big!"); - - /* Unmap pages. */ - ams_unmap_userpage(); - return 0; - } else if (emummc_cfg->base_cfg.type == EMUMMC_TYPE_FILES) { - /* Copy file dir path output to user. */ - memcpy(user_address, &emummc_cfg->file_cfg, sizeof(emummc_cfg->file_cfg)); - - /* Copy base config afterwards, since this can't fail. */ - memset(args, 0, sizeof(*args)); - memcpy(&args->X[1], emummc_cfg, sizeof(emummc_cfg->base_cfg)); - _Static_assert(sizeof(emummc_cfg->base_cfg) <= sizeof(*args) - sizeof(args->X[0]), "Emunand base config too big!"); - - /* Unmap pages. */ - ams_unmap_userpage(); - return 0; - } else { - /* Unmap pages. */ - ams_unmap_userpage(); - return 2; - } -} \ No newline at end of file diff --git a/exosphere/src/smc_ams.h b/exosphere/src/smc_ams.h deleted file mode 100644 index 7235f6125..000000000 --- a/exosphere/src/smc_ams.h +++ /dev/null @@ -1,30 +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_SMC_AMS_H -#define EXOSPHERE_SMC_AMS_H - -#include "smc_api.h" - -uint32_t ams_iram_copy(smc_args_t *args); -uint32_t ams_write_address(smc_args_t *args); - -uint32_t ams_get_emummc_config(smc_args_t *args); - -void ams_map_irampage(uintptr_t iram_address); -void ams_unmap_irampage(void); - -#endif \ No newline at end of file diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c deleted file mode 100644 index 4b3673f6c..000000000 --- a/exosphere/src/smc_api.c +++ /dev/null @@ -1,776 +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 <stdatomic.h> -#include <stdint.h> - -#include "utils.h" -#include "memory_map.h" - -#include "configitem.h" -#include "cpu_context.h" -#include "synchronization.h" -#include "masterkey.h" -#include "mc.h" -#include "mc0.h" -#include "mc1.h" -#include "memory_map.h" -#include "pmc.h" -#include "randomcache.h" -#include "sealedkeys.h" -#include "smc_api.h" -#include "smc_user.h" -#include "smc_ams.h" -#include "se.h" -#include "userpage.h" -#include "titlekey.h" -#include "sc7.h" -#include "exocfg.h" - -#define DEBUG_LOG_SMCS 0 -#define DEBUG_PANIC_ON_FAILURE 0 - -/* User SMC prototypes */ -uint32_t smc_set_config_user(smc_args_t *args); -uint32_t smc_get_config_user(smc_args_t *args); -uint32_t smc_check_status(smc_args_t *args); -uint32_t smc_get_result(smc_args_t *args); -uint32_t smc_exp_mod(smc_args_t *args); -uint32_t smc_get_random_bytes_for_user(smc_args_t *args); -uint32_t smc_generate_aes_kek(smc_args_t *args); -uint32_t smc_load_aes_key(smc_args_t *args); -uint32_t smc_crypt_aes(smc_args_t *args); -uint32_t smc_generate_specific_aes_key(smc_args_t *args); -uint32_t smc_compute_cmac(smc_args_t *args); -uint32_t smc_load_rsa_oaep_key(smc_args_t *args); -uint32_t smc_decrypt_rsa_private_key(smc_args_t *args); -uint32_t smc_load_secure_exp_mod_key(smc_args_t *args); -uint32_t smc_secure_exp_mod(smc_args_t *args); -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); -uint32_t smc_load_titlekey(smc_args_t *args); -uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args); - -/* 5.x SMC prototypes. */ -uint32_t smc_encrypt_rsa_key_for_import(smc_args_t *args); -uint32_t smc_decrypt_or_import_rsa_key(smc_args_t *args); - -/* Privileged SMC prototypes */ -uint32_t smc_cpu_suspend(smc_args_t *args); -uint32_t smc_cpu_off(smc_args_t *args); -uint32_t smc_cpu_on(smc_args_t *args); -uint32_t smc_get_config_priv(smc_args_t *args); -uint32_t smc_get_random_bytes_for_priv(smc_args_t *args); -uint32_t smc_panic(smc_args_t *args); -uint32_t smc_configure_carveout(smc_args_t *args); -uint32_t smc_read_write_register(smc_args_t *args); - -/* Atmosphere SMC prototypes */ -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); - -/* TODO: Provide a way to set this. It's 0 on non-recovery boot anyway... */ -static uint32_t g_smc_blacklist_mask = 0; - -typedef struct { - uint32_t id; - uint32_t blacklist_mask; - uint32_t (*handler)(smc_args_t *args); -} smc_table_entry_t; - -typedef struct { - smc_table_entry_t *handlers; - uint32_t num_handlers; -} smc_table_t; - -static smc_table_entry_t g_smc_user_table[] = { - {0, 4, NULL}, - {0xC3000401, 4, smc_set_config_user}, - {0xC3000002, 1, smc_get_config_user}, - {0xC3000003, 1, smc_check_status}, - {0xC3000404, 1, smc_get_result}, - {0xC3000E05, 4, smc_exp_mod}, - {0xC3000006, 1, smc_get_random_bytes_for_user}, - {0xC3000007, 1, smc_generate_aes_kek}, - {0xC3000008, 1, smc_load_aes_key}, - {0xC3000009, 1, smc_crypt_aes}, - {0xC300000A, 1, smc_generate_specific_aes_key}, - {0xC300040B, 1, smc_compute_cmac}, - {0xC300100C, 1, smc_load_rsa_oaep_key}, - {0xC300100D, 2, smc_decrypt_rsa_private_key}, - {0xC300100E, 4, smc_load_secure_exp_mod_key}, - {0xC300060F, 2, smc_secure_exp_mod}, - {0xC3000610, 4, smc_unwrap_rsa_oaep_wrapped_titlekey}, - {0xC3000011, 4, smc_load_titlekey}, - {0xC3000012, 4, smc_unwrap_aes_wrapped_titlekey} -}; -#define SMC_USER_HANDLERS (sizeof(g_smc_user_table) / sizeof(g_smc_user_table[0])) - -static smc_table_entry_t g_smc_priv_table[] = { - {0, 4, NULL}, - {0xC4000001, 4, smc_cpu_suspend}, - {0x84000002, 4, smc_cpu_off}, - {0xC4000003, 1, smc_cpu_on}, - {0xC3000004, 1, smc_get_config_priv}, - {0xC3000005, 1, smc_get_random_bytes_for_priv}, - {0xC3000006, 1, smc_panic}, - {0xC3000007, 1, smc_configure_carveout}, - {0xC3000008, 1, smc_read_write_register} -}; -#define SMC_PRIV_HANDLERS (sizeof(g_smc_priv_table) / sizeof(g_smc_priv_table[0])) - -/* This is a table used for atmosphere-specific SMCs. */ -static smc_table_entry_t g_smc_ams_table[] = { - {0, 4, NULL}, - {0xF0000201, 0, smc_ams_iram_copy}, - {0xF0000002, 0, smc_read_write_register}, - {0xF0000003, 0, smc_ams_write_address}, - {0xF0000404, 0, smc_ams_get_emummc_config}, -}; -#define SMC_AMS_HANDLERS (sizeof(g_smc_ams_table) / sizeof(g_smc_ams_table[0])) - -static smc_table_t g_smc_tables[SMC_HANDLER_COUNT + 1] = { - { /* SMC_HANDLER_USER */ - g_smc_user_table, - SMC_USER_HANDLERS - }, - { /* SMC_HANDLER_PRIV */ - g_smc_priv_table, - SMC_PRIV_HANDLERS - }, - { /* SMC_HANDLER_AMS */ - g_smc_ams_table, - SMC_AMS_HANDLERS - } -}; - -static atomic_flag g_is_user_smc_in_progress = ATOMIC_FLAG_INIT; -static atomic_flag g_is_priv_smc_in_progress = ATOMIC_FLAG_INIT; - -/* Global for smc_configure_carveout. */ -static bool g_configured_carveouts[2] = {false, false}; - -static bool g_has_suspended = false; -void set_suspend_for_debug(void) { - g_has_suspended = true; -} - -void set_version_specific_smcs(void) { - const uint32_t target_firmware = exosphere_get_target_firmware(); - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - /* No more LoadSecureExpModKey. */ - g_smc_user_table[0xE].handler = NULL; - g_smc_user_table[0xC].id = 0xC300D60C; - g_smc_user_table[0xC].handler = smc_encrypt_rsa_key_for_import; - g_smc_user_table[0xD].handler = smc_decrypt_or_import_rsa_key; - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_2_0_0) { - /* Nothing to do. */ - } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_1_0_0) { - /* 1.0.0 doesn't have ConfigureCarveout or ReadWriteRegister. */ - g_smc_priv_table[7].handler = NULL; - g_smc_priv_table[8].handler = NULL; - /* 1.0.0 doesn't have UnwrapAesWrappedTitlekey. */ - g_smc_user_table[0x12].handler = NULL; - } else { - panic_predefined(0xA); - } -} - - -uintptr_t get_smc_core012_stack_address(void) { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x1000; -} - -uintptr_t get_exception_entry_stack_address(unsigned int core_id) { - /* For core3, this is also the smc stack */ - if (core_id == 3) { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x1000; - } - else { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x80 * (core_id + 1); - } -} - -bool try_set_user_smc_in_progress(void) { - return lock_try_acquire(&g_is_user_smc_in_progress); -} -void set_user_smc_in_progress(void) { - lock_acquire(&g_is_user_smc_in_progress); -} -void clear_user_smc_in_progress(void) { - lock_release(&g_is_user_smc_in_progress); -} - -/* Privileged SMC lock must be available to exceptions.s. */ -void set_priv_smc_in_progress(void) { - lock_acquire(&g_is_priv_smc_in_progress); -} -void clear_priv_smc_in_progress(void) { - lock_release(&g_is_priv_smc_in_progress); -} - -uint32_t (*g_smc_callback)(void *, uint64_t) = NULL; -uint64_t g_smc_callback_key = 0; - -uint64_t try_set_smc_callback(uint32_t (*callback)(void *, uint64_t)) { - uint64_t key; - if (g_smc_callback_key) { - return 0; - } - - se_generate_random(KEYSLOT_SWITCH_RNGKEY, &key, sizeof(uint64_t)); - g_smc_callback_key = key; - g_smc_callback = callback; - return key; -} - -void clear_smc_callback(uint64_t key) { - if (g_smc_callback_key == key) { - g_smc_callback_key = 0; - } -} - -_Atomic uint64_t num_smcs_called = 0; - -void call_smc_handler(uint32_t handler_id, smc_args_t *args) { - unsigned char smc_id, call_range; - unsigned int result; - unsigned int (*smc_handler)(smc_args_t *args); - - /* Validate top-level handler. */ - if (handler_id >= SMC_HANDLER_COUNT) { - generic_panic(); - } - - /* If user-handler, detect if talking to Atmosphere/validate calling core. */ - if (handler_id == SMC_HANDLER_USER) { - if ((call_range = (unsigned char)((args->X[0] >> 24) & 0x3F)) == SMC_CALL_RANGE_TRUSTED_APP) { - /* Nintendo's SMCs are all OEM-specific. */ - /* Pending a reason not to, we will treat Trusted Application SMCs as intended to talk to Atmosphere. */ - handler_id = SMC_HANDLER_AMS; - } else if (get_core_id() != 3) { - /* USER SMCs must be called via svcCallSecureMonitor on core 3 (where spl runs) */ - generic_panic(); - } - } - - /* Validate sub-handler index */ - if ((smc_id = (unsigned char)args->X[0]) >= g_smc_tables[handler_id].num_handlers) { - generic_panic(); - } - - /* Validate sub-handler */ - if (g_smc_tables[handler_id].handlers[smc_id].id != args->X[0]) { - generic_panic(); - } - - /* Validate handler. */ - if ((smc_handler = g_smc_tables[handler_id].handlers[smc_id].handler) == NULL) { - generic_panic(); - } - - bool is_aes_kek = handler_id == SMC_HANDLER_USER && args->X[0] == 0xC3000007; - -#if DEBUG_LOG_SMCS - uint64_t num; - if (handler_id == SMC_HANDLER_USER) { - num = atomic_fetch_add(&num_smcs_called, 1); - *(volatile smc_args_t *)(get_iram_address_for_debug() + 0x100 + ((0x80 * num) & 0x3FFF)) = *args; - } -#endif - - /* Call function. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_8_0_0 || - (g_smc_tables[handler_id].handlers[smc_id].blacklist_mask & g_smc_blacklist_mask) == 0) { - args->X[0] = smc_handler(args); - } else { - /* Call not allowed due to current boot conditions. */ - args->X[0] = 6; - } - -#if DEBUG_LOG_SMCS - if (handler_id == SMC_HANDLER_USER) { - *(volatile smc_args_t *)(get_iram_address_for_debug() + 0x100 + ((0x80 * num + 0x40) & 0x3FFF)) = *args; - } -#endif - -#if DEBUG_PANIC_ON_FAILURE - if (args->X[0] && (!is_aes_kek || args->X[3] <= ATMOSPHERE_TARGET_FIRMWARE_CURRENT)) - { - MAKE_REG32(get_iram_address_for_debug() + 0x4FF0) = handler_id; - MAKE_REG32(get_iram_address_for_debug() + 0x4FF4) = smc_id; - MAKE_REG32(get_iram_address_for_debug() + 0x4FF8) = get_core_id(); - *(volatile smc_args_t *)(get_iram_address_for_debug() + 0x4F00) = *args; - panic(PANIC_REBOOT); - } -#else - (void)(is_aes_kek); -#endif - (void)result; /* FIXME: result unused */ -} - -uint32_t smc_wrapper_sync(smc_args_t *args, uint32_t (*handler)(smc_args_t *)) { - uint32_t result; - if (!try_set_user_smc_in_progress()) { - return 3; - } - result = handler(args); - clear_user_smc_in_progress(); - return result; -} - -uint32_t smc_wrapper_async(smc_args_t *args, uint32_t (*handler)(smc_args_t *), uint32_t (*callback)(void *, uint64_t)) { - uint32_t result; - uint64_t key; - if (!try_set_user_smc_in_progress()) { - return 3; - } - if ((key = try_set_smc_callback(callback)) != 0) { - result = handler(args); - if (result == 0) { - /* Pass the status check key back to userland. */ - args->X[1] = key; - /* Early return, leaving g_is_user_smc_in_progress locked */ - return result; - } else { - /* No status to check. */ - clear_smc_callback(key); - } - } else { - /* smcCheckStatus needs to be called. */ - result = 3; - } - clear_user_smc_in_progress(); - return result; -} - -uint32_t smc_set_config_user(smc_args_t *args) { - /* Actual value presumed in X3 on hardware. */ - return configitem_set(false, (ConfigItem)args->X[1], args->X[3]); -} - -uint32_t smc_get_config_user(smc_args_t *args) { - uint64_t out_item = 0; - uint32_t result; - result = configitem_get(false, (ConfigItem)args->X[1], &out_item); - args->X[1] = out_item; - return result; -} - -uint32_t smc_check_status(smc_args_t *args) { - if (g_smc_callback_key == 0) { - return 4; - } - - if (args->X[1] != g_smc_callback_key) { - return 5; - } - - args->X[1] = g_smc_callback(NULL, 0); - - g_smc_callback_key = 0; - return 0; -} - -uint32_t smc_get_result(smc_args_t *args) { - uint32_t status; - unsigned char result_buf[0x400]; - upage_ref_t page_ref; - - void *user_address = (void *)args->X[2]; - - if (g_smc_callback_key == 0) { - return 4; - } - - if (args->X[1] != g_smc_callback_key) { - return 5; - } - - /* Check result size */ - if (args->X[3] > 0x400) { - return 2; - } - - args->X[1] = g_smc_callback(result_buf, args->X[3]); - g_smc_callback_key = 0; - - /* Initialize page reference. */ - if (upage_init(&page_ref, user_address) == 0) { - return 2; - } - - /* Copy result output back to user. */ - if (secure_copy_to_user(&page_ref, user_address, result_buf, (size_t)args->X[3]) == 0) { - return 2; - } - - return 0; - (void)status; /* FIXME: status unused */ -} - -uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) { - uint32_t res = get_exp_mod_result(); - if (res == 0) { - if (size == 0x100) { - se_get_exp_mod_output(buf, 0x100); - /* smc_exp_mod is done now. */ - clear_user_smc_in_progress(); - res = 0; - } else { - res = 2; - } - } - return res; -} - -uint32_t smc_exp_mod(smc_args_t *args) { - return smc_wrapper_async(args, user_exp_mod, smc_exp_mod_get_result); -} - -uint32_t smc_get_random_bytes_for_user(smc_args_t *args) { - return smc_wrapper_sync(args, user_get_random_bytes); -} - -uint32_t smc_generate_aes_kek(smc_args_t *args) { - return smc_wrapper_sync(args, user_generate_aes_kek); -} - -uint32_t smc_load_aes_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_aes_key); -} - -uint32_t smc_crypt_aes_status_check(void *buf, uint64_t size) { - /* Buf and size are unused. */ - if (get_crypt_aes_done() != 1) { - return 3; - } - /* smc_crypt_aes is done now. */ - clear_user_smc_in_progress(); - return 0; -} - -uint32_t smc_crypt_aes(smc_args_t *args) { - return smc_wrapper_async(args, user_crypt_aes, smc_crypt_aes_status_check); -} - -uint32_t smc_generate_specific_aes_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_generate_specific_aes_key); -} - -uint32_t smc_compute_cmac(smc_args_t *args) { - return smc_wrapper_sync(args, user_compute_cmac); -} - -uint32_t smc_load_rsa_oaep_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_rsa_oaep_key); -} - -uint32_t smc_decrypt_rsa_private_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_decrypt_rsa_private_key); -} - -uint32_t smc_load_secure_exp_mod_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_secure_exp_mod_key); -} - -uint32_t smc_secure_exp_mod(smc_args_t *args) { - return smc_wrapper_async(args, user_secure_exp_mod, smc_exp_mod_get_result); -} - -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey_get_result(void *buf, uint64_t size) { - uint64_t *p_sealed_key = (uint64_t *)buf; - uint8_t rsa_wrapped_titlekey[0x100]; - uint8_t aes_wrapped_titlekey[0x10]; - uint8_t titlekey[0x10]; - uint64_t sealed_titlekey[2]; - uint32_t res = get_exp_mod_result(); - if (res == 0) { - if (size == 0x10) { - se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100); - if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) == 0x10) { - tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); - - p_sealed_key[0] = sealed_titlekey[0]; - p_sealed_key[1] = sealed_titlekey[1]; - - res = 0; - } else { - /* Failed to extract RSA OAEP wrapped key. */ - res = 2; - } - - /* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */ - clear_user_smc_in_progress(); - } else { - res = 2; - } - } - - return res; -} - -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { - return smc_wrapper_async(args, user_unwrap_rsa_oaep_wrapped_titlekey, smc_unwrap_rsa_oaep_wrapped_titlekey_get_result); -} - -uint32_t smc_load_titlekey(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_titlekey); -} - -uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args) { - return smc_wrapper_sync(args, user_unwrap_aes_wrapped_titlekey); -} - -uint32_t smc_encrypt_rsa_key_for_import(smc_args_t *args) { - return smc_wrapper_sync(args, user_encrypt_rsa_key_for_import); -} - -uint32_t smc_decrypt_or_import_rsa_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_decrypt_or_import_rsa_key); -} - - -uint32_t smc_cpu_on(smc_args_t *args) { - return cpu_on((uint32_t)args->X[1], args->X[2], args->X[3]); -} - -uint32_t smc_cpu_off(smc_args_t *args) { - return cpu_off(); -} - -/* Wrapper for cpu_suspend */ -uint32_t cpu_suspend_wrapper(smc_args_t *args) { - return cpu_suspend(args->X[1], args->X[2], args->X[3]); -} - -uint32_t smc_cpu_suspend(smc_args_t *args) { - return smc_wrapper_sync(args, cpu_suspend_wrapper); -} - -uint32_t smc_get_config_priv(smc_args_t *args) { - uint64_t out_item = 0; - uint32_t result; - result = configitem_get(true, (ConfigItem)args->X[1], &out_item); - args->X[1] = out_item; - return result; -} - -uint32_t smc_get_random_bytes_for_priv(smc_args_t *args) { - /* This is an interesting SMC. */ - /* The kernel must NEVER be unable to get random bytes, if it needs them */ - /* As such: */ - - uint32_t result; - - if (!try_set_user_smc_in_progress()) { - if (args->X[1] > 0x38) { - return 2; - } - /* Retrieve bytes from the cache. */ - size_t num_bytes = (size_t)args->X[1]; - randomcache_getbytes(&args->X[1], num_bytes); - result = 0; - } else { - /* If the kernel isn't denied service by a usermode SMC, generate fresh random bytes. */ - result = user_get_random_bytes(args); - /* Also, refill our cache while we have the chance in case we get denied later. */ - randomcache_refill(); - clear_user_smc_in_progress(); - } - return result; -} - -uint32_t smc_read_write_register(smc_args_t *args) { - uint64_t address = args->X[1]; - uint32_t mask = (uint32_t)(args->X[2]); - uint32_t value = (uint32_t)(args->X[3]); - volatile uint32_t *p_mmio = NULL; - /* Address must be aligned. */ - if (address & 3) { - return 2; - } - /* Check for PMC registers. */ - if (0x7000E400 <= address && address <= 0x7000EFFF) { - static const uint8_t pmc_whitelist[0x28] = { - 0xB9, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x80, 0x03, - 0x00, 0x00, 0x00, 0x17, 0x00, 0xC4, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00 - }; - /* Offset = Address - PMC_BASE */ - uint32_t offset = (uint32_t)(address - 0x7000E400); - uint32_t wl_ind = (offset >> 5); - /* If address is whitelisted, allow write. */ - if (wl_ind < sizeof(pmc_whitelist) && (pmc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) { - p_mmio = (volatile uint32_t *)(PMC_BASE + offset); - } else { - return 2; - } - } else { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - static const uint8_t mc_whitelist_5x[0xD00/(sizeof(uint32_t) * 8)] = { - 0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E, - 0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04, - 0x80, 0xFF, 0x08, 0x80, 0x03, 0x38, 0x8E, 0x1F, - 0xC8, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0xF0, 0x1F, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, - 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00 - }; - static const uint8_t mc01_whitelist_5x[0xC00/(sizeof(uint32_t) * 8)] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - }; - static const struct { - uint32_t phys_addr; - uint32_t size; - uint64_t virt_addr; - const uint8_t *whitelist; - } register_whitelists[3] = { - { MMIO_GET_DEVICE_PA(MMIO_DEVID_MC), sizeof(mc_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC), mc_whitelist_5x }, - { MMIO_GET_DEVICE_PA(MMIO_DEVID_MC0), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC0), mc01_whitelist_5x }, - { MMIO_GET_DEVICE_PA(MMIO_DEVID_MC1), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC1), mc01_whitelist_5x }, - }; - for (unsigned int which = 0; which < 3; which++) { - if (register_whitelists[which].phys_addr <= address && address < register_whitelists[which].phys_addr + register_whitelists[which].size) { - uint32_t offset = (uint32_t)(address - register_whitelists[which].phys_addr); - uint32_t wl_ind = (offset >> 5); - /* If address is whitelisted, allow write. */ - if (register_whitelists[which].whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) { - p_mmio = (volatile uint32_t *)(register_whitelists[which].virt_addr + offset); - } - break; - } - } - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - if (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + 0xD00) { - /* Memory Controller RW supported only on 4.0.0+ */ - static const uint8_t mc_whitelist[0x68] = { - 0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01, - 0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E, - 0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04, - 0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00, - 0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, - 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00 - }; - uint32_t offset = (uint32_t)(address - MMIO_GET_DEVICE_PA(MMIO_DEVID_MC)); - uint32_t wl_ind = (offset >> 5); - /* If address is whitelisted, allow write. */ - if (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) { - p_mmio = (volatile uint32_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC) + offset); - } - } - } - } - - /* Perform actual write. */ - if (p_mmio != NULL) { - uint32_t old_value; - /* Write whole value. */ - if (mask == 0xFFFFFFFF) { - old_value = 0; - } else { - old_value = *p_mmio; - } - if (mask) { - *p_mmio = (old_value & ~mask) | (value & mask); - } - /* Return old value. */ - args->X[1] = old_value; - return 0; - } else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0 && (address == 0x7001923C || address == 0x70019298)) { - /* These addresses are not allowed by the whitelist. */ - /* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */ - /* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */ - /* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */ - /* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */ - return 0; - } else { - return 2; - } -} - - -uint32_t smc_configure_carveout(smc_args_t *args) { - unsigned int carveout_id = (unsigned int)args->X[1]; - uint64_t address = args->X[2]; - uint64_t size = args->X[3]; - - /* Ensure carveout isn't too big. */ - if (size > KERNEL_CARVEOUT_SIZE_MAX) { - return 2; - } - - /* Ensure validity of carveout index. */ - if (carveout_id > 1) { - return 2; - } - - /* Configuration is one-shot, and cannot be done multiple times. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - if (g_configured_carveouts[carveout_id]) { - return 2; - } - } - - configure_kernel_carveout(carveout_id + 4, address, size); - g_configured_carveouts[carveout_id] = true; - return 0; -} - -uint32_t smc_panic(smc_args_t *args) { - /* Swap RGB values from args. */ - uint32_t color = ((args->X[1] & 0xF) << 8) | ((args->X[1] & 0xF0)) | ((args->X[1] & 0xF00) >> 8); - panic((color << 20) | 0x40); -} - -uint32_t smc_ams_iram_copy(smc_args_t *args) { - return smc_wrapper_sync(args, ams_iram_copy); -} - -uint32_t smc_ams_write_address(smc_args_t *args) { - return smc_wrapper_sync(args, ams_write_address); -} - -uint32_t smc_ams_get_emummc_config(smc_args_t *args) { - return smc_wrapper_sync(args, ams_get_emummc_config); -} diff --git a/exosphere/src/smc_api.h b/exosphere/src/smc_api.h deleted file mode 100644 index 862a28f5d..000000000 --- a/exosphere/src/smc_api.h +++ /dev/null @@ -1,57 +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_SMC_API_H -#define EXOSPHERE_SMC_API_H - -#include <stdint.h> - -#define SMC_HANDLER_USER 0 -#define SMC_HANDLER_PRIV 1 -#define SMC_HANDLER_COUNT 2 - -#define SMC_HANDLER_AMS (SMC_HANDLER_COUNT) - -#define SMC_CALL_RANGE_ARM_ARCH 0 -#define SMC_CALL_RANGE_CPU 1 -#define SMC_CALL_RANGE_SIP 2 -#define SMC_CALL_RANGE_OEM 3 -#define SMC_CALL_RANGE_STANDARD 4 - -#define SMC_CALL_RANGE_TRUSTED_APP 0x30 - -typedef struct { - uint64_t X[8]; -} smc_args_t; - -bool try_set_user_smc_in_progress(void); -void set_user_smc_in_progress(void); -void clear_user_smc_in_progress(void); - -void set_priv_smc_in_progress(void); -void clear_priv_smc_in_progress(void); - -uintptr_t get_smc_core012_stack_address(void); -uintptr_t get_exception_entry_stack_address(unsigned int core_id); - -void set_version_specific_smcs(void); - - -void set_suspend_for_debug(void); - -void call_smc_handler(unsigned int handler_id, smc_args_t *args); - -#endif diff --git a/exosphere/src/smc_user.c b/exosphere/src/smc_user.c deleted file mode 100644 index 8c0fba6d6..000000000 --- a/exosphere/src/smc_user.c +++ /dev/null @@ -1,1019 +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 <stdbool.h> -#include <stdint.h> -#include <string.h> - -#include "utils.h" -#include "arm.h" -#include "configitem.h" -#include "gcm.h" -#include "masterkey.h" -#include "smc_api.h" -#include "smc_user.h" -#include "se.h" -#include "fuse.h" -#include "sealedkeys.h" -#include "userpage.h" -#include "titlekey.h" -#include "exocfg.h" - -/* Globals. */ -static bool g_crypt_aes_done = false; -static uint32_t g_exp_mod_result = 0; - -static __attribute__((aligned(4))) uint8_t g_imported_exponents[4][0x100]; -static __attribute__((aligned(4))) uint8_t g_imported_moduli[4][0x100]; -static bool g_is_modulus_verified[4]; - -static __attribute__((aligned(4))) const uint8_t g_rsa_public_key[4] = { 0x00, 0x01, 0x00, 0x01 }; - -static __attribute__((aligned(4))) const uint8_t g_rsa_test_vector[0x100] = { - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', - 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D' -}; - -static uint32_t g_test_exp_mod_keyslot = 0; -static uint32_t g_test_exp_mod_usecase = 0; -static bool g_test_exp_mod_in_progress = false; - -static uint8_t g_rsausecase_to_cryptousecase[5] = {1, 2, 3, 5, 6}; - -static void import_rsa_exponent(unsigned int which, const uint8_t *exponent, uint64_t size) { - g_is_modulus_verified[which] = false; - for (unsigned int i = 0; i < 0x100; i++) { - g_imported_exponents[which][i] = exponent[i]; - g_imported_moduli[which][i] = 0; - } -} - -static void import_rsa_modulus(unsigned int which, const uint8_t *modulus, uint64_t size) { - uint64_t clamped_size = 0x100; - if (size <= 0x100) { - clamped_size = size; - } - if (clamped_size != 0) { - /* The official secure monitor implements this via bit-fiddling, */ - /* and to prevent accidental inaccuracy we will too. */ - /* It's probably done to prevent errors on negative sizes. */ - uint64_t remaining = 0x100; - if (size != 0x100 && (~size >= ~0xFFFFFFFFFFFFFEFFULL)) { - remaining = size; - } - memcpy(&g_imported_moduli[which][0], modulus, remaining); - } -} - -static bool load_imported_rsa_keypair(unsigned int keyslot, unsigned int which) { - if (!g_is_modulus_verified[which]) { - return false; - } - set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100); - return true; -} - -static void test_rsa_modulus_public(unsigned int which, unsigned int keyslot, const uint8_t *modulus, uint64_t modulus_size, unsigned int (*callback)(void)) { - import_rsa_modulus(which, modulus, modulus_size); - set_rsa_keyslot(keyslot, modulus, modulus_size, g_rsa_public_key, 0x4); - se_exp_mod(keyslot, g_rsa_test_vector, 0x100, callback); -} - -static void test_rsa_modulus_private(unsigned int which, unsigned int keyslot, unsigned int (*callback)(void)) { - uint8_t exponentiated_data[0x100]; - se_get_exp_mod_output(exponentiated_data, sizeof(exponentiated_data)); - set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100); - se_exp_mod(keyslot, exponentiated_data, 0x100, callback); -} - -static void validate_rsa_result(unsigned int which) { - char result[0x100]; - se_get_exp_mod_output(result, sizeof(result)); - if (memcmp(result, g_rsa_test_vector, sizeof(result)) == 0) { - g_is_modulus_verified[which] = true; - } -} - -static bool is_user_keyslot_valid(unsigned int keyslot) { - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - return keyslot <= 5; - } else { - return keyslot <= 3; - } -} - -void set_exp_mod_result(uint32_t result) { - g_exp_mod_result = result; -} - -uint32_t get_exp_mod_result(void) { - return g_exp_mod_result; -} - -uint32_t exp_mod_done_handler(void) { - set_exp_mod_result(0); - - se_trigger_interrupt(); - - return 0; -} - -static uint32_t test_exp_mod_done_handler(void) { - if (g_test_exp_mod_in_progress) { - g_test_exp_mod_in_progress = false; - test_rsa_modulus_private(g_test_exp_mod_usecase, g_test_exp_mod_keyslot, test_exp_mod_done_handler); - } else { - validate_rsa_result(g_test_exp_mod_usecase); - if (load_imported_rsa_keypair(g_test_exp_mod_keyslot, g_test_exp_mod_usecase)) { - se_exp_mod(g_test_exp_mod_keyslot, g_rsa_shared_data.storage_exp_mod.user_data, 0x100, exp_mod_done_handler); - } else { - set_exp_mod_result(2); - se_trigger_interrupt(); - } - } - - return 0; -} - -uint32_t user_exp_mod(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t exponent[0x100]; - uint8_t input[0x100]; - - upage_ref_t page_ref; - - /* Validate size. */ - if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) { - return 2; - } - - size_t exponent_size = (size_t)args->X[4]; - - void *user_input = (void *)args->X[1]; - void *user_exponent = (void *)args->X[2]; - void *user_modulus = (void *)args->X[3]; - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_input) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_result(3); - - /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size); - se_exp_mod(0, input, 0x100, exp_mod_done_handler); - - return 0; -} - -uint32_t user_get_random_bytes(smc_args_t *args) { - uint8_t random_bytes[0x40]; - if (args->X[1] > 0x38) { - return 2; - } - - size_t size = (size_t)args->X[1]; - - flush_dcache_range(random_bytes, random_bytes + size); - se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size); - flush_dcache_range(random_bytes, random_bytes + size); - - if (size != 0) { - memcpy(&args->X[1], random_bytes, size); - } - return 0; -} - -uint32_t user_generate_aes_kek(smc_args_t *args) { - uint64_t wrapped_kek[2]; - uint8_t kek_source[0x10]; - uint64_t kek[2]; - uint64_t sealed_kek[2]; - - wrapped_kek[0] = args->X[1]; - wrapped_kek[1] = args->X[2]; - - unsigned int master_key_rev = (unsigned int)args->X[3]; - - if (master_key_rev > 0) { - master_key_rev -= 1; /* GenerateAesKek offsets by one. */ - } - - if (master_key_rev >= MASTERKEY_REVISION_MAX || master_key_rev > mkey_get_revision()) { - return 2; - } - - uint64_t packed_options = args->X[4]; - if (packed_options > 0xFF) { - return 2; - } - - /* Switched the output based on how the system was booted. */ - uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); - - /* Switches the output based on how it will be used. */ - uint8_t usecase = (uint8_t)((packed_options >> 5) & (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0 ? 7 : 3)); - - /* Switched the output based on whether it should be console unique. */ - bool is_personalized = (int)(packed_options & 1); - - bool is_recovery_boot = configitem_is_recovery_boot(); - - /* 5.0.0+ Bounds checking. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - if (is_personalized) { - if (master_key_rev >= MASTERKEY_REVISION_MAX || (MASTERKEY_REVISION_300 <= master_key_rev && master_key_rev < MASTERKEY_REVISION_400_410)) { - return 2; - } - if (mask_id > 3 || usecase >= CRYPTOUSECASE_MAX_5X) { - return 2; - } - } else { - if (usecase >= CRYPTOUSECASE_UNK6) { - return 2; - } - if (usecase == CRYPTOUSECASE_UNK5 && mask_id >= 4) { - return 2; - } - } - } - - /* Mask 2 is only allowed when booted from recovery. */ - if (mask_id == 2 && !is_recovery_boot) { - return 2; - } - /* Mask 1 is only allowed when booted normally. */ - if (mask_id == 1 && is_recovery_boot) { - return 2; - } - - /* Masks 0, 3 are allowed all the time. */ - - const uint8_t kek_seeds[7][0x10] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}, - {0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C}, - {0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB}, - /* 5.0.0+ KEK seeds. */ - {0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31}, - {0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75}, - {0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61} - }; - const uint8_t kek_masks[4][0x10] = { - {0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}, - {0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77}, - {0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81}, - {0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B} - }; - - /* Create kek source. */ - for (unsigned int i = 0; i < 0x10; i++) { - kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i]; - } - - unsigned int keyslot; - if (is_personalized) { - /* Behavior changed in 4.0.0, and in 5.0.0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - keyslot = devkey_get_keyslot(master_key_rev); - } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - if (master_key_rev >= 1) { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ - } else { - keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ - } - } else { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; - } - } else { - keyslot = mkey_get_keyslot(master_key_rev); - } - - /* Derive kek. */ - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10); - - - /* Seal kek. */ - seal_key(sealed_kek, 0x10, kek, 0x10, usecase); - - args->X[1] = sealed_kek[0]; - args->X[2] = sealed_kek[1]; - - return 0; -} - -uint32_t user_load_aes_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - - uint32_t keyslot = (uint32_t)args->X[1]; - if (!is_user_keyslot_valid(keyslot)) { - return 2; - } - - /* Copy keydata */ - sealed_kek[0] = args->X[2]; - sealed_kek[1] = args->X[3]; - wrapped_key[0] = args->X[4]; - wrapped_key[1] = args->X[5]; - - /* Unseal the kek. */ - unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); - - /* Unwrap the key. */ - decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10); - return 0; -} - - -void set_crypt_aes_done(bool done) { - g_crypt_aes_done = done; -} - -bool get_crypt_aes_done(void) { - return g_crypt_aes_done; -} - -uint32_t crypt_aes_done_handler(void) { - se_check_for_error(); - - set_crypt_aes_done(true); - - se_trigger_interrupt(); - - return 0; -} - -uint32_t user_crypt_aes(smc_args_t *args) { - uint32_t keyslot = args->X[1] & 3; - uint32_t mode = (args->X[1] >> 4) & 3; - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - keyslot = args->X[1] & 7; - } - - uint64_t iv_ctr[2]; - iv_ctr[0] = args->X[2]; - iv_ctr[1] = args->X[3]; - - uint32_t in_ll_paddr = (uint32_t)(args->X[4]); - uint32_t out_ll_paddr = (uint32_t)(args->X[5]); - - size_t size = args->X[6]; - if (!is_user_keyslot_valid(keyslot) || size & 0xF) { - return 2; - } - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - /* Disallow dma lists outside of safe range. */ - if (in_ll_paddr - 0x80000000 >= 0x3FF7F5) { - return 2; - } - if (out_ll_paddr - 0x80000000 >= 0x3FF7F5) { - return 2; - } - } - - set_crypt_aes_done(false); - - uint64_t result = 0; - switch (mode) { - case 0: /* CBC Encryption */ - se_aes_cbc_encrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 1: /* CBC Decryption */ - se_aes_cbc_decrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 2: /* CTR "Encryption" */ - se_aes_ctr_crypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 3: - default: - result = 1; - break; - } - - return result; -} - -uint32_t user_generate_specific_aes_key(smc_args_t *args) { - uint64_t wrapped_key[2]; - uint8_t key[0x10]; - unsigned int master_key_rev; - bool should_mask; - - wrapped_key[0] = args->X[1]; - wrapped_key[1] = args->X[2]; - - master_key_rev = (unsigned int)args->X[3]; - if (master_key_rev > 0) { - master_key_rev -= 1; - } - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - master_key_rev = 0; - } - - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - - if (args->X[4] > 1) { - return 2; - } - should_mask = args->X[4] != 0; - - unsigned int keyslot; - - /* Behavior changed in 5.0.0. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - keyslot = devkey_get_keyslot(master_key_rev); - } else if (exosphere_get_target_firmware() == ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - if (master_key_rev >= 1) { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ - } else { - keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ - } - } else { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; - } - - if (fuse_get_bootrom_patch_version() < 0x7F) { - /* On dev units, use a fixed "all-zeroes" seed. */ - /* Yes, this data really is all-zero in actual TrustZone .rodata. */ - uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t dev_specific_aes_key_ctr[0x10] = {0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9}; - uint8_t dev_specific_aes_key_mask[0x10] = {0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E}; - - flush_dcache_range(key, key + 0x10); - se_aes_ctr_crypt(keyslot, key, 0x10, dev_specific_aes_key_source, 0x10, dev_specific_aes_key_ctr, 0x10); - flush_dcache_range(key, key + 0x10); - - if (should_mask) { - for (unsigned int i = 0; i < 0x10; i++) { - key[i] ^= dev_specific_aes_key_mask[i]; - } - } - } else { - /* On retail, standard kek->key decryption. */ - uint8_t retail_specific_aes_key_source[0x10] = {0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95}; - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, retail_specific_aes_key_source, 0x10); - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, key, 0x10, wrapped_key, 0x10); - } - - - args->X[1] = read64le(key, 0); - args->X[2] = read64le(key, 8); - return 0; -} - -uint32_t user_compute_cmac(smc_args_t *args) { - uint32_t keyslot = (uint32_t)args->X[1]; - void *user_address = (void *)args->X[2]; - size_t size = (size_t)args->X[3]; - - uint8_t user_data[0x400]; - uint64_t result_cmac[2]; - upage_ref_t page_ref; - - /* Validate keyslot and size. */ - if (!is_user_keyslot_valid(keyslot) || args->X[3] > 0x400) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size); - - /* Copy CMAC out. */ - args->X[1] = result_cmac[0]; - args->X[2] = result_cmac[1]; - - return 0; -} - -uint32_t user_load_rsa_oaep_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - generic_panic(); - } - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (is_personalized && size != 0x240) { - return 2; - } - if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - /* Ensure that our private key is 0x100 bytes. */ - if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized, NULL) < 0x100) { - return 2; - } - - memcpy(g_imported_exponents[0], user_data, 0x100); - return 0; -} - -uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - generic_panic(); - } - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (size > 0x240) { - return 2; - } - - if (is_personalized && size < 0x31) { - return 2; - } - if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - size_t out_size; - - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized, NULL)) == 0) { - return 2; - } - - if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { - return 2; - } - - args->X[1] = out_size; - return 0; -} - -uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - /* This function no longer exists in 5.x+. */ - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - generic_panic(); - } - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (is_personalized && size != 0x130) { - return 2; - } - if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - size_t out_size; - - /* Ensure that our key is non-zero bytes. */ - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized, NULL)) == 0) { - return 2; - } - - /* Copy key to global. */ - if (out_size <= 0x100) { - memcpy(g_imported_exponents[1], user_data, out_size); - } else { - memcpy(g_imported_exponents[1], user_data, 0x100); - } - - return 0; -} - -uint32_t user_secure_exp_mod(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t input[0x100]; - - upage_ref_t page_ref; - - void *user_input = (void *)args->X[1]; - void *user_modulus = (void *)args->X[2]; - - unsigned int exponent_id = 1; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - switch (args->X[3]) { - case 0: - exponent_id = 1; - break; - case 1: - exponent_id = 2; - break; - case 2: - exponent_id = 3; - break; - default: - return 2; - } - } - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_input) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_result(3); - - /* Hardcode RSA keyslot 0. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { - set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100); - se_exp_mod(0, input, 0x100, exp_mod_done_handler); - } else if (load_imported_rsa_keypair(0, exponent_id)) { - se_exp_mod(0, input, 0x100, exp_mod_done_handler); - } else { - memcpy(g_rsa_shared_data.storage_exp_mod.user_data, input, 0x100); - g_test_exp_mod_keyslot = 0; - g_test_exp_mod_usecase = exponent_id; - g_test_exp_mod_in_progress = true; - test_rsa_modulus_public(exponent_id, 0, modulus, 0x100, test_exp_mod_done_handler); - } - - return 0; -} - -uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t wrapped_key[0x100]; - - upage_ref_t page_ref; - - void *user_wrapped_key = (void *)args->X[1]; - void *user_modulus = (void *)args->X[2]; - unsigned int option = (unsigned int)args->X[7]; - unsigned int master_key_rev; - unsigned int titlekey_type; - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - master_key_rev = option & 0x3F; - titlekey_type = (option >> 6) & 1; - } else { - master_key_rev = option; - titlekey_type = 0; - } - - if(master_key_rev > 0) { - master_key_rev -= 1; - } - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - } else { - master_key_rev = 0; - } - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_wrapped_key) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, wrapped_key, user_wrapped_key, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_result(3); - - /* Expected label_hash occupies args->X[3] to args->X[6]. */ - tkey_set_expected_label_hash(&args->X[3]); - - tkey_set_master_key_rev(master_key_rev); - tkey_set_type(titlekey_type); - - /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[0], 0x100); - se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler); - - return 0; - -} - -uint32_t user_load_titlekey(smc_args_t *args) { - uint64_t sealed_titlekey[2]; - - uint32_t keyslot = (uint32_t)args->X[1]; - if (!is_user_keyslot_valid(keyslot)) { - return 2; - } - - /* Copy keydata */ - sealed_titlekey[0] = args->X[2]; - sealed_titlekey[1] = args->X[3]; - - /* Unseal the key. */ - unseal_titlekey(keyslot, sealed_titlekey, 0x10); - return 0; - -} - -uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { - uint64_t aes_wrapped_titlekey[2]; - uint8_t titlekey[0x10]; - uint64_t sealed_titlekey[2]; - - aes_wrapped_titlekey[0] = args->X[1]; - aes_wrapped_titlekey[1] = args->X[2]; - unsigned int master_key_rev = (unsigned int)args->X[3]; - - if (master_key_rev > 0) { - master_key_rev -= 1; - } - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - } else { - master_key_rev = 0; - } - - tkey_set_master_key_rev(master_key_rev); - tkey_set_type(0); - - tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); - - args->X[1] = sealed_titlekey[0]; - args->X[2] = sealed_titlekey[1]; - - return 0; /* FIXME: what should we return there */ -} - - -uint32_t user_encrypt_rsa_key_for_import(smc_args_t *args) { - uint64_t in_sealed_kek[2]; - uint64_t out_sealed_kek[2]; - uint64_t in_wrapped_key[2]; - uint64_t out_wrapped_key[2]; - uint8_t usecase; - - uint8_t user_data[0x400]; - void *user_address; - void *user_in_kek; - void *user_out_kek; - void *user_in_key; - void *user_out_key; - size_t size; - upage_ref_t page_ref; - - /* Copy keydata */ - user_in_kek = (void *)args->X[1]; - user_out_kek = (void *)args->X[2]; - usecase = args->X[3] & 7; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - user_in_key = (void *)args->X[6]; - user_out_key = (void *)args->X[7]; - - if (usecase > CRYPTOUSECASE_RSAIMPORT) { - return 2; - } - if (usecase == 0) { - if (size < 0x31 || size > 0x240) { - return 2; - } - } else if (size < 0x130 || size > 0x240) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 - || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0 - || user_copy_to_secure(&page_ref, in_sealed_kek, user_in_kek, 0x10) == 0 - || user_copy_to_secure(&page_ref, out_sealed_kek, user_out_kek, 0x10) == 0 - || user_copy_to_secure(&page_ref, in_wrapped_key, user_in_key, 0x10) == 0 - || user_copy_to_secure(&page_ref, out_wrapped_key, user_out_key, 0x10) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - size_t out_size; - - uint8_t device_id_high; - - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, in_sealed_kek, 0x10, in_wrapped_key, 0x10, CRYPTOUSECASE_RSAIMPORT, true, &device_id_high)) == 0) { - return 2; - } - - gcm_encrypt_key(user_data, size, user_data, size - 0x30, out_sealed_kek, 0x10, out_wrapped_key, 0x10, g_rsausecase_to_cryptousecase[usecase], device_id_high); - - if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { - return 2; - } - - return 0; -} - -uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - uint8_t usecase; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - /* This function only exists in 5.x+. */ - if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - generic_panic(); - } - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - usecase = args->X[3] & 7; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (usecase > CRYPTOUSECASE_RSAIMPORT) { - return 2; - } - if (usecase == 0) { - if (size < 0x31 || size > 0x240) { - return 2; - } - } else if (size < 0x130 || size > 0x240) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - - size_t out_size; - - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, g_rsausecase_to_cryptousecase[usecase], true, NULL)) == 0) { - return 2; - } - - unsigned int exponent_id; - bool import_modulus; - - switch (usecase) { - case 0: - if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { - return 2; - } - return 0; - case 1: - exponent_id = 1; - import_modulus = false; - break; - case 2: - exponent_id = 0; - import_modulus = true; - break; - case 3: - exponent_id = 2; - import_modulus = false; - break; - case 4: - exponent_id = 3; - import_modulus = true; - break; - default: - generic_panic(); - } - - /* Modulus import isn't implemented on < 10.0.0. */ - import_modulus &= (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0); - - /* Import the key. */ - import_rsa_exponent(exponent_id, user_data, 0x100); - if (import_modulus) { - import_rsa_modulus(exponent_id, user_data + 0x100, 0x100); - g_is_modulus_verified[exponent_id] = true; - } - return 0; - -} diff --git a/exosphere/src/smc_user.h b/exosphere/src/smc_user.h deleted file mode 100644 index b100f6618..000000000 --- a/exosphere/src/smc_user.h +++ /dev/null @@ -1,47 +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_SMC_USER_H -#define EXOSPHERE_SMC_USER_H - -#include "smc_api.h" - -uint32_t user_exp_mod(smc_args_t *args); -uint32_t user_get_random_bytes(smc_args_t *args); -uint32_t user_generate_aes_kek(smc_args_t *args); -uint32_t user_load_aes_key(smc_args_t *args); -uint32_t user_crypt_aes(smc_args_t *args); -uint32_t user_generate_specific_aes_key(smc_args_t *args); -uint32_t user_compute_cmac(smc_args_t *args); -uint32_t user_load_rsa_oaep_key(smc_args_t *args); -uint32_t user_decrypt_rsa_private_key(smc_args_t *args); -uint32_t user_load_secure_exp_mod_key(smc_args_t *args); -uint32_t user_secure_exp_mod(smc_args_t *args); -uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); -uint32_t user_load_titlekey(smc_args_t *args); -uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args); - -uint32_t user_encrypt_rsa_key_for_import(smc_args_t *args); -uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args); - - -void set_crypt_aes_done(bool done); -bool get_crypt_aes_done(void); - -void set_exp_mod_result(uint32_t result); -uint32_t get_exp_mod_result(void); - -#endif \ No newline at end of file diff --git a/exosphere/src/start.s b/exosphere/src/start.s deleted file mode 100644 index 84eb1c2cd..000000000 --- a/exosphere/src/start.s +++ /dev/null @@ -1,300 +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/>. - */ - -/* 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. */ - /* mask all interrupts */ - msr daifset, 0b1111 - - /* - 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 .cold_crt0.text.start, "ax", %progbits -.align 6 -.global __start_cold -__start_cold: - ERRATUM_INVALIDATE_BTB_AT_BOOT - - /* - This coldboot crt0 doesn't enter the boot critical section in the official monitor. - However we'll initialize g_boot_critical_section so that it acts like core0 has entered it, - for it to be in .data and for safety. - */ - - /* Relocate the crt0. Nintendo doesn't do it. */ - ldr x0, =__cold_crt0_start__ - adr x1, __start_cold - ldr x2, =__cold_crt0_end__ - cmp x0, x1 - beq _post_cold_crt0_reloc - 1: - ldp x3, x4, [x1], #0x10 - stp x3, x4, [x0], #0x10 - cmp x0, x2 - blo 1b - - adr x19, __start_cold - adr x20, g_coldboot_crt0_relocation_list - sub x20, x20, x19 - ldr x16, =_post_cold_crt0_reloc - br x16 - -_post_cold_crt0_reloc: - /* Setup stack for coldboot crt0. */ - msr spsel, #0 - bl get_coldboot_crt0_temp_stack_address - mov sp, x0 - mov fp, #0 - bl get_coldboot_crt0_stack_address - mov sp, x0 - mov fp, #0 - - /* Relocate Exosphere image to free DRAM, clearing the image in IRAM. */ - ldr x0, =0x80010000 - add x20, x20, x0 - ldr x2, =__loaded_end_lma__ - ldr x3, =__glob_origin__ - sub x21, x2, x3 - mov x1, x19 - mov x2, x21 - add x2, x2, x0 - 2: - ldp x3, x4, [x1] - stp x3, x4, [x0], #0x10 - stp xzr, xzr, [x1], #0x10 - cmp x0, x2 - blo 2b - - /* X0 = TZ-in-DRAM, X1 = relocation-list-in-DRAM. */ - mov x0, x20 - ldr x1, =0x80010000 - /* Set size in coldboot relocation list. */ - str x21, [x0, #0x8] - - bl coldboot_init - - ldr x16, =__jump_to_main_cold - br x16 - -.section .warm_crt0.text.start, "ax", %progbits -.align 6 -.global __start_warm -__start_warm: - ERRATUM_INVALIDATE_BTB_AT_BOOT - - /* For some reasons, Nintendo uses spsel, #1 around here, causing issues if an exception occurs */ - msr spsel, #0 - - /* Nintendo doesn't use anything like the following two lines, but their critical section algo is borked */ - /* FWIW this function doesn't use a stack atm, with latest GCC, but that might change. */ - bl get_warmboot_crt0_stack_address_critsec_enter - mov sp, x0 - - /* PA(__main_start__) = __warmboot_crt0_start__ + 0x800 (refer to the linker script) */ - ldr x0, =g_boot_critical_section - bl warmboot_crt0_critical_section_enter - - bl get_warmboot_crt0_stack_address - mov sp, x0 - mov fp, #0 - - bl warmboot_init - ldr x16, =__jump_to_main_warm - br x16 - -/* Used by coldboot as well */ -.section .warm_crt0.text.__set_memory_registers, "ax", %progbits -.align 4 -.global __set_memory_registers -.type __set_memory_registers, %function -__set_memory_registers: - msr cpuectlr_el1, x2 - isb - msr scr_el3, x3 - msr ttbr0_el3, x0 - msr tcr_el3, x4 - msr cptr_el3, x5 - msr mair_el3, x6 - msr vbar_el3, x1 - - /* Invalidate the entire TLB on the Inner Shareable domain */ - isb - dsb ish - tlbi alle3is - dsb ish - isb - - msr sctlr_el3, x7 - isb - ret - -.section .text.__jump_to_main_cold, "ax", %progbits -.align 4 -__jump_to_main_cold: - /* This is inspired by Nintendo's code but significantly different */ - bl __set_exception_entry_stack_pointer - /* - Normally Nintendo calls it in crt0, but it's fine to do that here. - Please note that package2.c shouldn't have constructed objects, because we - call __libc_fini_array after load_package2 has been cleared, on EL3 - to EL3 chainload. - */ - bl __libc_init_array - - bl get_pk2ldr_stack_address - mov sp, x0 - - mov x0, x20 - bl load_package2 - - mov w0, #3 /* use core3 stack temporarily */ - bl get_exception_entry_stack_address - mov sp, x0 - bl coldboot_main - /* If we ever return, it's to chainload an EL3 payload */ - bl __libc_fini_array - /* Reset the core (only one is running on coldboot) */ - RESET_CORE - -.section .text.__jump_to_main_warm, "ax", %progbits -__jump_to_main_warm: - /* Nintendo doesn't do that here, causing issues if an exception occurs */ - bl __set_exception_entry_stack_pointer - - mov w0, #0 /* use core0,1,2 stack bottom + 0x800 (VA of warmboot crt0 sp) temporarily */ - bl get_warmboot_main_stack_address - mov sp, x0 - bl warmboot_main - -.section .text.__set_exception_entry_stack, "ax", %progbits -.type __set_exception_entry_stack, %function -.global __set_exception_entry_stack -__set_exception_entry_stack_pointer: - /* If SPSel == 1 on entry, make sure your function doesn't use stack variables! */ - mov x16, lr - mrs x17, spsel - mrs x0, mpidr_el1 - and w0, w0, #3 - bl get_exception_entry_stack_address - msr spsel, #1 - mov sp, x0 - msr spsel, x17 - mov lr, x16 - ret - -.section .text.__jump_to_lower_el, "ax", %progbits -.global __jump_to_lower_el -.type __jump_to_lower_el, %function -__jump_to_lower_el: - /* x0: arg (context ID), x1: entrypoint, w2: spsr */ - mov x19, x0 - mov w2, w2 - - msr elr_el3, x1 - msr spsr_el3, x2 - - bl __set_exception_entry_stack_pointer - mov x0, x19 - - isb - eret - -/* Custom stuff */ -.section .cold_crt0.data.g_coldboot_crt0_relocation_list, "aw", %progbits -.align 3 -.global g_coldboot_crt0_relocation_list -g_coldboot_crt0_relocation_list: - .quad 0, 0 /* __start_cold, to be set & loaded size */ - .quad 1, 5 /* number of sections to relocate/clear before & after mmu init */ - /* Relocations */ - .quad __warmboot_crt0_start__, __warmboot_crt0_end__, __warmboot_crt0_lma__ - .quad __main_start__, __main_bss_start__, __main_lma__ - .quad __pk2ldr_start__, __pk2ldr_bss_start__, __pk2ldr_lma__ - .quad __vectors_start__, __vectors_end__, __vectors_lma__ - /* BSS clears */ - .quad __main_bss_start__, __main_end__, 0 - .quad __pk2ldr_bss_start__, __pk2ldr_end__, 0 - -/* Critical section */ -.section .warm_crt0.data.g_boot_critical_section, "aw", %progbits -.align 2 -.global g_boot_critical_section -g_boot_critical_section: - .word 1 /* Core0 entered, by default. */ diff --git a/exosphere/src/synchronization.h b/exosphere/src/synchronization.h deleted file mode 100644 index 602bbccf7..000000000 --- a/exosphere/src/synchronization.h +++ /dev/null @@ -1,139 +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_SYNCHRONIZATION_H -#define EXOSPHERE_SYNCHRONIZATION_H - -#include <stdatomic.h> -#include "utils.h" - -/* Simple atomics driver for Exosphere. */ - -typedef struct { - struct { - uint8_t ticket_number : 7; - uint8_t is_entering : 1; - } customers[4]; -} critical_section_t; - -static inline void __dsb_sy(void) { - __asm__ __volatile__ ("dsb sy" ::: "memory"); -} - -static inline void __dsb_ish(void) { - __asm__ __volatile__ ("dsb ish" ::: "memory"); -} - -static inline void __dmb_sy(void) { - __asm__ __volatile__ ("dmb sy" ::: "memory"); -} - -static inline void __isb(void) { - __asm__ __volatile__ ("isb" ::: "memory"); -} - -static inline void __sev(void) { - __asm__ __volatile__ ("sev"); -} - -static inline void __sevl(void) { - __asm__ __volatile__ ("sevl"); -} - -static inline void __wfe(void) { - __asm__ __volatile__ ("wfe"); -} - -/* Acquire a lock. */ -static inline void lock_acquire(atomic_flag *flag) { - while (atomic_flag_test_and_set_explicit(flag, memory_order_acquire)) { - /* Wait to acquire lock. */ - } -} - -/* Release a lock. */ -static inline void lock_release(atomic_flag *flag) { - atomic_flag_clear_explicit(flag, memory_order_release); -} - -/* Try to acquire a lock. */ -static inline bool lock_try_acquire(atomic_flag *flag) { - return !atomic_flag_test_and_set_explicit(flag, memory_order_acquire); -} - -/* - Enter a critical section, using the Lamport's bakery algorithm. - https://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm - - This is invoked on warmboot before the MMU is turned on, therefore - exclusive load/store instructions can't be used. - - Note: Nintendo has tried to implement that algorithm, but it seems that - they didn't understand how it works, and their implementation - is therefore a complete failure: in particular, the "ticket number" is - always the same (core0 will always enter the critical section before all the cores, - and so on...), and thus there can be starvation, etc. - - Nintendo, wtf. -*/ -ALINLINE static inline unsigned int critical_section_enter(volatile critical_section_t *critical_section) { - unsigned int id = get_core_id(); - uint8_t my_ticket_number = 0; - uint8_t tmp; - - critical_section->customers[id].is_entering = 1; - for (unsigned int i = 0; i < 4; i++) { - tmp = critical_section->customers[id].ticket_number; - my_ticket_number = tmp > my_ticket_number ? tmp : my_ticket_number; - } - - critical_section->customers[id].ticket_number = ++my_ticket_number; - critical_section->customers[id].is_entering = 0; - __dsb_sy(); - __sev(); - - for (unsigned int i = 0; i < 4; i++) { - __sevl(); - do { - __wfe(); - } while (critical_section->customers[i].is_entering); - - __sevl(); - do { - __wfe(); - tmp = critical_section->customers[i].ticket_number; - } while (tmp && (tmp < my_ticket_number || (tmp == my_ticket_number && i < id))); - } - - __dmb_sy(); - return id; -} - -/* - Leaves a critical section, using the Lamport's bakery algorithm (see above). - - Note: Nintendo failed even that: they're clearing the entire critical section state - instead of just the counter associated to the current core. - - Nintendo, wtf. -*/ -ALINLINE static inline void critical_section_leave(volatile critical_section_t *critical_section) { - critical_section->customers[get_core_id()].ticket_number = 0; - __dsb_sy(); - __sev(); -} - -#endif diff --git a/exosphere/src/sysctr0.h b/exosphere/src/sysctr0.h deleted file mode 100644 index c8e78568e..000000000 --- a/exosphere/src/sysctr0.h +++ /dev/null @@ -1,49 +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_SYSCTR0_H -#define EXOSPHERE_SYSCTR0_H - -#include <stdint.h> - -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 SYSCTR0 Registers. */ - -#define SYSCTR0_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_SYSCTR0)) - -#define MAKE_SYSCTR0_REG(n) MAKE_REG32(SYSCTR0_BASE + n) - -#define SYSCTR0_CNTCR_0 MAKE_SYSCTR0_REG(0x00) -#define SYSCTR0_CNTSR_0 MAKE_SYSCTR0_REG(0x04) -#define SYSCTR0_CNTCV0_0 MAKE_SYSCTR0_REG(0x08) -#define SYSCTR0_CNTCV1_0 MAKE_SYSCTR0_REG(0x0C) -#define SYSCTR0_CNTFID0_0 MAKE_SYSCTR0_REG(0x20) -#define SYSCTR0_CNTFID1_0 MAKE_SYSCTR0_REG(0x24) -#define SYSCTR0_COUNTERID4_0 MAKE_SYSCTR0_REG(0xFD0) -#define SYSCTR0_COUNTERID5_0 MAKE_SYSCTR0_REG(0xFD4) -#define SYSCTR0_COUNTERID6_0 MAKE_SYSCTR0_REG(0xFD8) -#define SYSCTR0_COUNTERID7_0 MAKE_SYSCTR0_REG(0xFDC) -#define SYSCTR0_COUNTERID0_0 MAKE_SYSCTR0_REG(0xFE0) -#define SYSCTR0_COUNTERID1_0 MAKE_SYSCTR0_REG(0xFE4) -#define SYSCTR0_COUNTERID2_0 MAKE_SYSCTR0_REG(0xFE8) -#define SYSCTR0_COUNTERID3_0 MAKE_SYSCTR0_REG(0xFEC) -#define SYSCTR0_COUNTERID8_0 MAKE_SYSCTR0_REG(0xFF0) -#define SYSCTR0_COUNTERID9_0 MAKE_SYSCTR0_REG(0xFF4) -#define SYSCTR0_COUNTERID10_0 MAKE_SYSCTR0_REG(0xFF8) -#define SYSCTR0_COUNTERID11_0 MAKE_SYSCTR0_REG(0xFFC) - -#endif diff --git a/exosphere/src/sysreg.h b/exosphere/src/sysreg.h deleted file mode 100644 index 760ccc01d..000000000 --- a/exosphere/src/sysreg.h +++ /dev/null @@ -1,49 +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_SYSREG_H -#define EXOSPHERE_SYSREG_H - -#include <stdint.h> - -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 System Registers. */ - -#define SYSREG_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_SYSREGS)) - -#define SB_BASE (SYSREG_BASE + 0x200) - -#define MAKE_SYSREG(n) MAKE_REG32(SYSREG_BASE + n) -#define MAKE_SB_REG(n) MAKE_REG32(SB_BASE + n) - -#define AHB_ARBITRATION_DISABLE_0 MAKE_SYSREG(0x004) - -#define SB_CSR_0 MAKE_SB_REG(0x00) -#define SB_PIROM_START_0 MAKE_SB_REG(0x04) -#define SB_PFCFG_0 MAKE_SB_REG(0x08) -#define SB_SECURE_SPAREREG_0_0 MAKE_SB_REG(0x0C) -#define SB_SECURE_SPAREREG_1_0 MAKE_SB_REG(0x10) -#define SB_SECURE_SPAREREG_2_0 MAKE_SB_REG(0x14) -#define SB_SECURE_SPAREREG_3_0 MAKE_SB_REG(0x18) -#define SB_SECURE_SPAREREG_4_0 MAKE_SB_REG(0x1C) -#define SB_SECURE_SPAREREG_5_0 MAKE_SB_REG(0x20) -#define SB_SECURE_SPAREREG_6_0 MAKE_SB_REG(0x24) -#define SB_SECURE_SPAREREG_7_0 MAKE_SB_REG(0x28) -#define SB_AA64_RESET_LOW_0 MAKE_SB_REG(0x30) -#define SB_AA64_RESET_HIGH_0 MAKE_SB_REG(0x34) - -#endif diff --git a/exosphere/src/timers.c b/exosphere/src/timers.c deleted file mode 100644 index b8884ad84..000000000 --- a/exosphere/src/timers.c +++ /dev/null @@ -1,41 +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 <stdbool.h> - -#include "utils.h" -#include "timers.h" - -void wait(uint32_t microseconds) { - uint32_t old_time = TIMERUS_CNTR_1US_0; - while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { - /* Spin-lock. */ - } -} - -__attribute__ ((noreturn)) void watchdog_reboot(void) { - unsigned int current_core = get_core_id(); - volatile watchdog_timers_t *wdt = GET_WDT(current_core); - wdt->PATTERN = WDT_REBOOT_PATTERN; - wdt->COMMAND = 2; /* Disable Counter. */ - GET_WDT_REBOOT_CFG_REG(current_core) = 0xC0000000; - wdt->CONFIG = 0x8015 + current_core; /* Full System Reset after Fourth Counter expires, using TIMER(5+core_id). */ - wdt->COMMAND = 1; /* Enable Counter. */ - while (true) { - /* Wait for reboot. */ - } -} \ No newline at end of file diff --git a/exosphere/src/timers.h b/exosphere/src/timers.h deleted file mode 100644 index f98f9f7b5..000000000 --- a/exosphere/src/timers.h +++ /dev/null @@ -1,111 +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_TIMERS_H -#define EXOSPHERE_TIMERS_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 Timers. */ - -static inline uintptr_t get_timers_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_TMRs_WDTs); -} - -static inline uintptr_t get_rtc_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC); -} - -#define TIMERS_BASE (get_timers_base()) -#define MAKE_TIMERS_REG(n) MAKE_REG32(TIMERS_BASE + n) - -#define TIMERUS_CNTR_1US_0 MAKE_TIMERS_REG(0x10) -#define TIMERUS_USEC_CFG_0 MAKE_TIMERS_REG(0x14) -#define SHARED_INTR_STATUS_0 MAKE_TIMERS_REG(0x1A0) -#define SHARED_TIMER_SECURE_CFG_0 MAKE_TIMERS_REG(0x1A4) - -#define RTC_BASE (get_rtc_base()) -#define MAKE_RTC_REG(n) MAKE_REG32(RTC_BASE + n) - -#define RTC_SECONDS MAKE_RTC_REG(0x08) -#define RTC_SHADOW_SECONDS MAKE_RTC_REG(0x0C) -#define RTC_MILLI_SECONDS MAKE_RTC_REG(0x10) - -typedef struct { - uint32_t CONFIG; - uint32_t STATUS; - uint32_t COMMAND; - uint32_t PATTERN; -} watchdog_timers_t; - -#define GET_WDT(n) ((volatile watchdog_timers_t *)(TIMERS_BASE + 0x100 + 0x20 * n)) -#define WDT_REBOOT_PATTERN 0xC45A -#define GET_WDT_REBOOT_CFG_REG(n) MAKE_REG32(TIMERS_BASE + 0x60 + 0x8 * n) - -void wait(uint32_t microseconds); - -static inline uint32_t get_time_s(void) { - return RTC_SECONDS; -} - -static inline uint32_t get_time_ms(void) { - return (RTC_MILLI_SECONDS | (RTC_SHADOW_SECONDS << 10)); -} - -static inline uint32_t get_time_us(void) { - return TIMERUS_CNTR_1US_0; -} - -/** - * Returns the time in microseconds. - */ -static inline uint32_t get_time(void) { - return get_time_us(); -} - -/** - * Returns the number of microseconds that have passed since a given get_time(). - */ -static inline uint32_t get_time_since(uint32_t base) { - return get_time_us() - base; -} - -/** - * Delays for a given number of microseconds. - */ -static inline void udelay(uint32_t usecs) { - uint32_t start = get_time_us(); - while (get_time_us() - start < usecs); -} - -/** - * Delays until a number of usecs have passed since an absolute start time. - */ -static inline void udelay_absolute(uint32_t start, uint32_t usecs) { - while (get_time_us() - start < usecs); -} - -/** - * Delays for a given number of milliseconds. - */ -static inline void mdelay(uint32_t msecs) { - uint32_t start = get_time_ms(); - while (get_time_ms() - start < msecs); -} - -__attribute__ ((noreturn)) void watchdog_reboot(void); -#endif diff --git a/exosphere/src/titlekey.c b/exosphere/src/titlekey.c deleted file mode 100644 index 991123572..000000000 --- a/exosphere/src/titlekey.c +++ /dev/null @@ -1,180 +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 <string.h> - -#include "utils.h" -#include "arm.h" -#include "exocfg.h" - -#include "titlekey.h" -#include "masterkey.h" -#include "se.h" - -/* Set the expected db prefix. */ -void tkey_set_expected_label_hash(uint64_t *label_hash) { - for (unsigned int i = 0; i < 4; i++) { - g_rsa_shared_data.unwrap_titlekey.expected_label_hash[i] = label_hash[i]; - } -} - -void tkey_set_master_key_rev(unsigned int master_key_rev) { - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - generic_panic(); - } - g_rsa_shared_data.unwrap_titlekey.master_key_rev = master_key_rev; -} - -static void tkey_validate_type(unsigned int type) { - if (type > TITLEKEY_TYPE_MAX || (type > 0 && exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_6_0_0)) { - generic_panic(); - } -} - -void tkey_set_type(unsigned int type) { - tkey_validate_type(type); - g_rsa_shared_data.unwrap_titlekey.type = type; -} - -/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */ -void calculate_mgf1_and_xor(void *masked, size_t masked_size, const void *seed, size_t seed_size) { - uint8_t cur_hash[0x20]; - uint8_t hash_buf[0xE4]; - if (seed_size >= 0xE0) { - generic_panic(); - } - - size_t hash_buf_size = seed_size + 4; - if (seed_size != 0) { - memcpy(hash_buf, seed, seed_size); - } - uint32_t round_num = 0; - - uint8_t *p_out = (uint8_t *)masked; - - while (masked_size) { - size_t cur_size = masked_size; - if (cur_size > 0x20) { - cur_size = 0x20; - } - - write32be(hash_buf, seed_size, round_num++); - - flush_dcache_range(hash_buf, hash_buf + hash_buf_size); - se_calculate_sha256(cur_hash, hash_buf, hash_buf_size); - - for (unsigned int i = 0; i < cur_size; i++) { - *p_out ^= cur_hash[i]; - p_out++; - } - - masked_size -= cur_size; - } -} - -size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size) { - if (src_size != 0x100) { - generic_panic(); - } - - /* RSA Wrapped titlekeys use RSA-OAEP. */ - /* Message is of the form prefix || maskedSalt || maskedDB. */ - /* maskedSalt = salt ^ MGF1(maskedDB) */ - /* maskedDB = DB ^ MGF1(salt) */ - /* Salt is random and not validated in any way. */ - /* DB is of the form label_hash || 00....01 || wrapped_titlekey. */ - /* label_hash is, in practice, a constant in es .rodata. */ - /* I have no idea why Nintendo did this, it should be either nonconstant (in tik) or in tz .rodata. */ - - uint8_t *message = (uint8_t *)src; - - /* Prefix should always be zero. */ - if (*message != 0) { - return 0; - } - - - uint8_t *salt = message + 1; - uint8_t *db = message + 0x21; - - /* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */ - uint8_t *expected_label_hash = (uint8_t *)(&g_rsa_shared_data.unwrap_titlekey.expected_label_hash[0]); - - /* Unmask the salt. */ - calculate_mgf1_and_xor(salt, 0x20, db, 0xDF); - /* Unmask the DB. */ - calculate_mgf1_and_xor(db, 0xDF, salt, 0x20); - - /* Validate expected salt. */ - for (unsigned int i = 0; i < 0x20; i++) { - if (expected_label_hash[i] != db[i]) { - return 0; - } - } - - /* Don't validate salt from message[1:0x21] at all. */ - - /* Advance pointer to DB, since we've validated the salt prefix. */ - db += 0x20; - - /* DB must be of the form 0000...01 || wrapped_titlekey */ - if (*db != 0) { - return 0; - } - - /* Locate wrapped_titlekey inside DB. */ - size_t wrapped_key_offset_in_db = 0; - while (wrapped_key_offset_in_db < 0xBF) { - if (db[wrapped_key_offset_in_db] == 0) { - wrapped_key_offset_in_db++; - } else if (db[wrapped_key_offset_in_db] == 1) { - wrapped_key_offset_in_db++; - break; - } else { - /* Invalid wrapped titlekey prefix. */ - return 0; - } - } - - /* Validate size... */ - size_t wrapped_titlekey_size = 0xBF - wrapped_key_offset_in_db; - if (wrapped_titlekey_size > dst_size || wrapped_titlekey_size == 0) { - return 0; - } - - /* Extract the wrapped key. */ - memcpy(dst, &db[wrapped_key_offset_in_db], wrapped_titlekey_size); - return wrapped_titlekey_size; -} - -static const uint8_t titlekek_sources[TITLEKEY_TYPE_MAX+1][0x10] = { - {0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B}, - {0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D} -}; - -void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) { - if (g_rsa_shared_data.unwrap_titlekey.master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - /* Generate the appropriate titlekek into keyslot 9. */ - unsigned int master_keyslot = mkey_get_keyslot(g_rsa_shared_data.unwrap_titlekey.master_key_rev); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_sources[g_rsa_shared_data.unwrap_titlekey.type], 0x10); - - /* Unwrap the titlekey using the titlekek. */ - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); -} diff --git a/exosphere/src/titlekey.h b/exosphere/src/titlekey.h deleted file mode 100644 index 4d33783dc..000000000 --- a/exosphere/src/titlekey.h +++ /dev/null @@ -1,33 +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_TITLEKEY_H -#define EXOSPHERE_TITLEKEY_H - -#include <stdint.h> -#include "rsa_common.h" - -#define TITLEKEY_TYPE_MAX 0x1 - -void tkey_set_expected_label_hash(uint64_t *label_hash); -void tkey_set_master_key_rev(unsigned int master_key_rev); -void tkey_set_type(unsigned int type); - -size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size); - -void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size); - -#endif \ No newline at end of file diff --git a/exosphere/src/uart.c b/exosphere/src/uart.c deleted file mode 100644 index 020dbd583..000000000 --- a/exosphere/src/uart.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2018 naehrwert - * 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 "uart.h" -#include "timers.h" -#include "pinmux.h" - -static inline void uart_wait_cycles(uint32_t baud, uint32_t num) -{ - udelay((num * 1000000 + 16 * baud - 1) / (16 * baud)); -} - -static inline void uart_wait_syms(uint32_t baud, uint32_t num) -{ - udelay((num * 1000000 + baud - 1) / baud); -} - -void uart_config(UartDevice dev) { - volatile tegra_pinmux_t *pinmux = pinmux_get_regs(); - - switch (dev) { - case UART_A: - pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0); - pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - break; - case UART_B: - pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - break; - case UART_C: - pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0); - break; - case UART_D: - pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0); - break; - case UART_E: - /* Unused. */ - break; - default: break; - } -} - -void uart_init(UartDevice dev, uint32_t baud) { - volatile tegra_uart_t *uart = uart_get_regs(dev); - - /* Wait for idle state. */ - uart_wait_idle(dev, UART_VENDOR_STATE_TX_IDLE); - - /* Calculate baud rate, round to nearest. */ - uint32_t rate = (8 * baud + 408000000) / (16 * baud); - - /* Setup UART in FIFO mode. */ - uart->UART_IER_DLAB = 0; - uart->UART_MCR = 0; - uart->UART_LCR = (UART_LCR_DLAB | UART_LCR_WD_LENGTH_8); /* Enable DLAB and set word length 8. */ - uart->UART_THR_DLAB = (uint8_t)rate; /* Divisor latch LSB. */ - uart->UART_IER_DLAB = (uint8_t)(rate >> 8); /* Divisor latch MSB. */ - uart->UART_LCR &= ~(UART_LCR_DLAB); /* Disable DLAB. */ - uart->UART_SPR; /* Dummy read. */ - uart_wait_syms(baud, 3); /* Wait for 3 symbols at the new baudrate. */ - - /* Enable FIFO with default settings. */ - uart->UART_IIR_FCR = UART_FCR_FCR_EN_FIFO; - uart->UART_SPR; /* Dummy read as mandated by TRM. */ - uart_wait_cycles(baud, 3); /* Wait for 3 baud cycles, as mandated by TRM (erratum). */ - - /* Flush FIFO. */ - uart_wait_idle(dev, UART_VENDOR_STATE_TX_IDLE); /* Make sure there's no data being written in TX FIFO (TRM). */ - uart->UART_IIR_FCR |= UART_FCR_RX_CLR | UART_FCR_TX_CLR; /* Clear TX and RX FIFOs. */ - uart_wait_cycles(baud, 32); /* Wait for 32 baud cycles (TRM, erratum). */ - /* Wait for idle state (TRM). */ - uart_wait_idle(dev, UART_VENDOR_STATE_TX_IDLE | UART_VENDOR_STATE_RX_IDLE); -} - -/* This function blocks until the UART device is in the desired state. */ -void uart_wait_idle(UartDevice dev, UartVendorStatus status) { - volatile tegra_uart_t *uart = uart_get_regs(dev); - - if (status & UART_VENDOR_STATE_TX_IDLE) { - while (!(uart->UART_LSR & UART_LSR_TMTY)) { - /* Wait */ - } - } - if (status & UART_VENDOR_STATE_RX_IDLE) { - while (uart->UART_LSR & UART_LSR_RDR) { - /* Wait */ - } - } -} - -void uart_send(UartDevice dev, const void *buf, size_t len) { - volatile tegra_uart_t *uart = uart_get_regs(dev); - - for (size_t i = 0; i < len; i++) { - while (!(uart->UART_LSR & UART_LSR_THRE)) { - /* Wait until it's possible to send data. */ - } - uart->UART_THR_DLAB = *((const uint8_t *)buf + i); - } -} - -void uart_recv(UartDevice dev, void *buf, size_t len) { - volatile tegra_uart_t *uart = uart_get_regs(dev); - - for (size_t i = 0; i < len; i++) { - while (!(uart->UART_LSR & UART_LSR_RDR)) { - /* Wait until it's possible to receive data. */ - } - *((uint8_t *)buf + i) = uart->UART_THR_DLAB; - } -} diff --git a/exosphere/src/uart.h b/exosphere/src/uart.h deleted file mode 100644 index 3dd90f7f2..000000000 --- a/exosphere/src/uart.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2018 naehrwert - * 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_UART_H -#define EXOSPHERE_UART_H - -#include <stdint.h> -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 UARTs. */ - -static inline uintptr_t get_uart_base(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_UART); -} - -#define UART_BASE (get_uart_base()) - -#define BAUD_115200 115200 - -/* UART devices */ -typedef enum { - UART_A = 0, - UART_B = 1, - UART_C = 2, - UART_D = 3, - UART_E = 4, -} UartDevice; - -/* 36.3.12 UART_VENDOR_STATUS_0_0 */ -typedef enum { - UART_VENDOR_STATE_TX_IDLE = 1 << 0, - UART_VENDOR_STATE_RX_IDLE = 1 << 1, - - /* This bit is set to 1 when a read is issued to an empty FIFO and gets cleared on register read (sticky bit until read) - 0 = NO_UNDERRUN - 1 = UNDERRUN - */ - UART_VENDOR_STATE_RX_UNDERRUN = 1 << 2, - - /* This bit is set to 1 when write data is issued to the TX FIFO when it is already full and gets cleared on register read (sticky bit until read) - 0 = NO_OVERRUN - 1 = OVERRUN - */ - UART_VENDOR_STATE_TX_OVERRUN = 1 << 3, - - UART_VENDOR_STATE_RX_FIFO_COUNTER = 0b111111 << 16, /* reflects number of current entries in RX FIFO */ - UART_VENDOR_STATE_TX_FIFO_COUNTER = 0b111111 << 24 /* reflects number of current entries in TX FIFO */ -} UartVendorStatus; - -/* 36.3.6 UART_LSR_0 */ -typedef enum { - UART_LSR_RDR = 1 << 0, /* Receiver Data Ready */ - UART_LSR_OVRF = 1 << 1, /* Receiver Overrun Error */ - UART_LSR_PERR = 1 << 2, /* Parity Error */ - UART_LSR_FERR = 1 << 3, /* Framing Error */ - UART_LSR_BRK = 1 << 4, /* BREAK condition detected on line */ - UART_LSR_THRE = 1 << 5, /* Transmit Holding Register is Empty -- OK to write data */ - UART_LSR_TMTY = 1 << 6, /* Transmit Shift Register empty status */ - UART_LSR_FIFOE = 1 << 7, /* Receive FIFO Error */ - UART_LSR_TX_FIFO_FULL = 1 << 8, /* Transmitter FIFO full status */ - UART_LSR_RX_FIFO_EMPTY = 1 << 9, /* Receiver FIFO empty status */ -} UartLineStatus; - -/* 36.3.4 UART_LCR_0 */ -typedef enum { - UART_LCR_WD_LENGTH_5 = 0, /* word length 5 */ - UART_LCR_WD_LENGTH_6 = 1, /* word length 6 */ - UART_LCR_WD_LENGTH_7 = 2, /* word length 7 */ - UART_LCR_WD_LENGTH_8 = 3, /* word length 8 */ - - /* STOP: - 0 = Transmit 1 stop bit - 1 = Transmit 2 stop bits (receiver always checks for 1 stop bit) - */ - UART_LCR_STOP = 1 << 2, - UART_LCR_PAR = 1 << 3, /* Parity enabled */ - UART_LCR_EVEN = 1 << 4, /* Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1) */ - UART_LCR_SET_P = 1 << 5, /* Set (force) parity to value in LCR[4] */ - UART_LCR_SET_B = 1 << 6, /* Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK */ - UART_LCR_DLAB = 1 << 7, /* Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors) */ -} UartLineControl; - -/* 36.3.3 UART_IIR_FCR_0 */ -typedef enum { - UART_FCR_FCR_EN_FIFO = 1 << 0, /* Enable the transmit and receive FIFOs. This bit should be enabled */ - UART_FCR_RX_CLR = 1 << 1, /* Clears the contents of the receive FIFO and resets its counter logic to 0 (the receive shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ - UART_FCR_TX_CLR = 1 << 2, /* Clears the contents of the transmit FIFO and resets its counter logic to 0 (the transmit shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ - - /* DMA: - 0 = DMA_MODE_0 - 1 = DMA_MODE_1 - */ - UART_FCR_DMA = 1 << 3, - - /* TX_TRIG - 0 = FIFO_COUNT_GREATER_16 - 1 = FIFO_COUNT_GREATER_8 - 2 = FIFO_COUNT_GREATER_4 - 3 = FIFO_COUNT_GREATER_1 - */ - UART_FCR_TX_TRIG = 3 << 4, - UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4, - UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4, - UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4, - UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4, - - /* RX_TRIG - 0 = FIFO_COUNT_GREATER_1 - 1 = FIFO_COUNT_GREATER_4 - 2 = FIFO_COUNT_GREATER_8 - 3 = FIFO_COUNT_GREATER_16 - */ - UART_FCR_RX_TRIG = 3 << 6, - UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6, - UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6, - UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6, - UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6, -} UartFifoControl; - -/* 36.3.3 UART_IIR_FCR_0 */ -typedef enum { - UART_IIR_IS_STA = 1 << 0, /* Interrupt Pending if ZERO */ - UART_IIR_IS_PRI0 = 1 << 1, /* Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3] */ - UART_IIR_IS_PRI1 = 1 << 2, /* Encoded Interrupt ID Refer to IIR[3:0] table */ - UART_IIR_IS_PRI2 = 1 << 3, /* Encoded Interrupt ID Refer to IIR[3:0] table */ - - /* FIFO Mode Status - 0 = 16450 mode (no FIFO) - 1 = 16550 mode (FIFO) - */ - UART_IIR_EN_FIFO = 3 << 6, - UART_IIR_MODE_16450 = 0 << 6, - UART_IIR_MODE_16550 = 1 << 6, -} UartInterruptIdentification; - -typedef struct { - uint32_t UART_THR_DLAB; - uint32_t UART_IER_DLAB; - uint32_t UART_IIR_FCR; - uint32_t UART_LCR; - uint32_t UART_MCR; - uint32_t UART_LSR; - uint32_t UART_MSR; - uint32_t UART_SPR; - uint32_t UART_IRDA_CSR; - uint32_t UART_RX_FIFO_CFG; - uint32_t UART_MIE; - uint32_t UART_VENDOR_STATUS; - uint8_t _0x30[0x0C]; - uint32_t UART_ASR; -} tegra_uart_t; - -void uart_config(UartDevice dev); -void uart_init(UartDevice dev, uint32_t baud); -void uart_wait_idle(UartDevice dev, UartVendorStatus status); -void uart_send(UartDevice dev, const void *buf, size_t len); -void uart_recv(UartDevice dev, void *buf, size_t len); - -static inline volatile tegra_uart_t *uart_get_regs(UartDevice dev) { - static const size_t offsets[] = {0, 0x40, 0x200, 0x300, 0x400}; - return (volatile tegra_uart_t *)(UART_BASE + offsets[dev]); -} - -#endif diff --git a/exosphere/src/userpage.c b/exosphere/src/userpage.c deleted file mode 100644 index c34e5fdb7..000000000 --- a/exosphere/src/userpage.c +++ /dev/null @@ -1,93 +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 <string.h> - -#include "utils.h" -#include "userpage.h" -#include "memory_map.h" -#include "arm.h" - -static uintptr_t g_user_page_user_address = 0ull; - -static inline uintptr_t get_page_for_address(const void *address) { - return ((uintptr_t)(address)) & ~0xFFFULL; -} - -/* Create a user page reference for the desired address. */ -/* Returns 1 on success, 0 on failure. */ -bool upage_init(upage_ref_t *upage, void *user_address) { - upage->user_address = get_page_for_address(user_address); - upage->secure_monitor_address = 0ull; - - if (g_user_page_user_address != 0ull) { - /* Different physical address indicate SPL was rebooted, or another process got access to svcCallSecureMonitor. Panic. */ - if (g_user_page_user_address != upage->user_address) { - generic_panic(); - } - upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; - } else { - /* Validate SPL's physically random address (must be in DRAM (supports up to 6GB, retail console have 4GB) and page-aligned). */ - if ((upage->user_address - 0x80000000ull) < (6ull << 30) && ((uintptr_t)user_address & 0xFFF) == 0) { - static const uint64_t userpage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_NORMAL; - uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - g_user_page_user_address = upage->user_address; - mmu_map_page(mmu_l3_tbl, USER_PAGE_SECURE_MONITOR_ADDR, upage->user_address, userpage_attributes); - tlb_invalidate_page_inner_shareable((void *)USER_PAGE_SECURE_MONITOR_ADDR); - upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; - } else { - generic_panic(); - } - } - - return upage->secure_monitor_address != 0ull; -} - -bool user_copy_to_secure(upage_ref_t *upage, void *secure_dst, const void *user_src, size_t size) { - /* Fail if the page doesn't match. */ - if (get_page_for_address(user_src) != upage->user_address) { - return false; - } - - /* Fail if we go past the page boundary. */ - if (size != 0 && get_page_for_address(user_src + size - 1) != upage->user_address) { - return false; - } - - const void *secure_src = (const void *)(upage->secure_monitor_address + ((uintptr_t)user_src - upage->user_address)); - if (size != 0) { - memcpy(secure_dst, secure_src, size); - } - return true; -} - -bool secure_copy_to_user(upage_ref_t *upage, void *user_dst, const void *secure_src, size_t size) { - /* Fail if the page doesn't match. */ - if (get_page_for_address(user_dst) != upage->user_address) { - return false; - } - - /* Fail if we go past the page boundary. */ - if (size != 0 && get_page_for_address(user_dst + size - 1) != upage->user_address) { - return false; - } - - void *secure_dst = (void *)(upage->secure_monitor_address + ((uintptr_t)user_dst - upage->user_address)); - if(size != 0) { - memcpy(secure_dst, secure_src, size); - } - return true; -} diff --git a/exosphere/src/userpage.h b/exosphere/src/userpage.h deleted file mode 100644 index 076d85844..000000000 --- a/exosphere/src/userpage.h +++ /dev/null @@ -1,39 +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_USERPAGE_H -#define EXOSPHERE_USERPAGE_H - -#include "utils.h" -#include "memory_map.h" - -static inline uintptr_t get_user_page_secure_monitor_addr(void) { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_USERPAGE); -} - -#define USER_PAGE_SECURE_MONITOR_ADDR (get_user_page_secure_monitor_addr()) - -typedef struct { - uintptr_t user_address; - uintptr_t secure_monitor_address; -} upage_ref_t; - -bool upage_init(upage_ref_t *user_page, void *user_address); - -bool user_copy_to_secure(upage_ref_t *user_page, void *secure_dst, const void *user_src, size_t size); -bool secure_copy_to_user(upage_ref_t *user_page, void *user_dst, const void *secure_src, size_t size); - -#endif diff --git a/exosphere/src/utils.c b/exosphere/src/utils.c deleted file mode 100644 index 938f80d0e..000000000 --- a/exosphere/src/utils.c +++ /dev/null @@ -1,80 +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 <stdbool.h> -#include <string.h> -#include "utils.h" -#include "se.h" -#include "fuse.h" -#include "pmc.h" -#include "timers.h" - -#define SAVE_SYSREG64(reg, ofs) do { __asm__ __volatile__ ("mrs %0, " #reg : "=r"(temp_reg) :: "memory"); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + ofs) = (uint32_t)((temp_reg >> 0) & 0xFFFFFFFFULL); MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + ofs + 4) = (uint32_t)((temp_reg >> 32) & 0xFFFFFFFFULL); } while(false) - -__attribute__ ((noreturn)) void panic(uint32_t code) { - /* Set Panic Code for NX_BOOTLOADER. */ - if (APBDEV_PMC_SCRATCH200_0 == 0) { - APBDEV_PMC_SCRATCH200_0 = code; - } - - /* // Uncomment for Debugging. - uint64_t temp_reg; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM)) = APBDEV_PMC_SCRATCH200_0; - SAVE_SYSREG64(ESR_EL3, 0x10); - SAVE_SYSREG64(ELR_EL3, 0x18); - SAVE_SYSREG64(FAR_EL3, 0x20); - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2; - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; */ - - - /* TODO: Custom Panic Driver, which displays to screen without rebooting. */ - /* For now, just use NX BOOTLOADER's panic. */ - fuse_disable_programming(); - APBDEV_PMC_CRYPTO_OP_0 = 1; /* Disable all SE operations. */ - // while (1) { } - watchdog_reboot(); -} - -__attribute__ ((noreturn)) void generic_panic(void) { - /* //Uncomment for Debugging. - uint64_t temp_reg; - do { __asm__ __volatile__ ("mov %0, LR" : "=r"(temp_reg) :: "memory"); } while (false); - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28) = (uint32_t)((temp_reg >> 0) & 0xFFFFFFFFULL); - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x28 + 4) = (uint32_t)((temp_reg >> 32) & 0xFFFFFFFFULL); - do { __asm__ __volatile__ ("mov %0, SP" : "=r"(temp_reg) :: "memory"); } while (false); - for (unsigned int i = 0; i < 0x80; i += 4) { - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM) + 0x40 + i) = *((volatile uint32_t *)(temp_reg + i)); - } */ - panic(0xFF000006); -} - -__attribute__ ((noreturn)) void panic_predefined(uint32_t which) { - static const uint32_t codes[0x10] = {COLOR_0, COLOR_1, COLOR_2, COLOR_3, COLOR_4, COLOR_5, COLOR_6, COLOR_7, COLOR_8, COLOR_9, COLOR_A, COLOR_B, COLOR_C, COLOR_D, COLOR_E, COLOR_F}; - panic(codes[which & 0xF]); -} - -__attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be) -{ - if(as <= bs && bs < ae) - return true; - if(bs <= as && as < be) - return true; - return false; -} - -uintptr_t get_iram_address_for_debug(void) { - return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_DEBUG_IRAM); -} diff --git a/exosphere/src/utils.h b/exosphere/src/utils.h deleted file mode 100644 index f95fd1e65..000000000 --- a/exosphere/src/utils.h +++ /dev/null @@ -1,134 +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_UTILS_H -#define EXOSPHERE_UTILS_H - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "panic_color.h" - -#define BIT(n) (1u << (n)) -#define BITL(n) (1ull << (n)) -#define MASK(n) (BIT(n) - 1) -#define MASKL(n) (BITL(n) - 1) -#define MASK2(a,b) (MASK(a) & ~MASK(b)) -#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) - -#define MAKE_REG32(a) (*(volatile uint32_t *)(a)) - -#define ALIGN(m) __attribute__((aligned(m))) -#define PACKED __attribute__((packed)) - -#define ALINLINE __attribute__((always_inline)) - -#define SET_SYSREG(reg, val) do { temp_reg = (val); __asm__ __volatile__ ("msr " #reg ", %0" :: "r"(temp_reg) : "memory"); } while(false) - -/* Custom stuff below */ - -/* For coldboot */ -typedef struct { - uint8_t *vma; - uint8_t *end_vma; - uintptr_t lma; -} coldboot_crt0_reloc_t; - -typedef struct { - uintptr_t reloc_base; - size_t loaded_bin_size; - size_t nb_relocs_pre_mmu_init; /* first is always warmboot_crt0 */ - size_t nb_relocs_post_mmu_init; /* first is always main segment excl. .bss */ - coldboot_crt0_reloc_t relocs[]; -} coldboot_crt0_reloc_list_t; - -__attribute__ ((noreturn)) void panic(uint32_t code); -__attribute__ ((noreturn)) void generic_panic(void); -__attribute__ ((noreturn)) void panic_predefined(uint32_t which); -bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); - -uintptr_t get_iram_address_for_debug(void); - -static inline uintptr_t get_physical_address(const void *vaddr) { - uintptr_t PAR; - __asm__ __volatile__ ("at s1e3r, %0" :: "r"(vaddr)); - __asm__ __volatile__ ("mrs %0, par_el1" : "=r"(PAR)); - return (PAR & 1) ? 0ull : (PAR & MASK2L(40, 12)) | ((uintptr_t)vaddr & MASKL(12)); -} - -static inline uintptr_t get_physical_address_el0(const uintptr_t el0_vaddr) { - uintptr_t PAR; - __asm__ __volatile__ ("at s1e0r, %0" :: "r"(el0_vaddr)); - __asm__ __volatile__ ("mrs %0, par_el1" : "=r"(PAR)); - return (PAR & 1) ? 0ull : (PAR & MASK2L(40, 12)) | ((uintptr_t)el0_vaddr & MASKL(12)); -} - -static inline uint32_t read32le(const volatile void *dword, size_t offset) { - return *(uint32_t *)((uintptr_t)dword + offset); -} - -static inline uint32_t read32be(const volatile void *dword, size_t offset) { - return __builtin_bswap32(read32le(dword, offset)); -} - -static inline uint64_t read64le(const volatile void *qword, size_t offset) { - return *(uint64_t *)((uintptr_t)qword + offset); -} - -static inline uint64_t read64be(const volatile void *qword, size_t offset) { - return __builtin_bswap64(read64le(qword, offset)); -} - -static inline void write32le(volatile void *dword, size_t offset, uint32_t value) { - *(uint32_t *)((uintptr_t)dword + offset) = value; -} - -static inline void write32be(volatile void *dword, size_t offset, uint32_t value) { - write32le(dword, offset, __builtin_bswap32(value)); -} - -static inline void write64le(volatile void *qword, size_t offset, uint64_t value) { - *(uint64_t *)((uintptr_t)qword + offset) = value; -} - -static inline void write64be(volatile void *qword, size_t offset, uint64_t value) { - write64le(qword, offset, __builtin_bswap64(value)); -} - -static inline unsigned int get_core_id(void) { - uint64_t core_id; - __asm__ __volatile__ ("mrs %0, mpidr_el1" : "=r"(core_id)); - return (unsigned int)core_id & 3; -} - -static inline uint64_t get_debug_authentication_status(void) { - uint64_t debug_auth; - __asm__ __volatile__ ("mrs %0, dbgauthstatus_el1" : "=r"(debug_auth)); - return debug_auth; -} - -static inline uint32_t get_spsr(void) { - uint32_t spsr; - __asm__ __volatile__ ("mrs %0, spsr_el3" : "=r"(spsr)); - return spsr; -} - -static inline bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { - return __builtin_add_overflow_p(a, b, (uint32_t)0); -} - -#endif diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c deleted file mode 100644 index b7cce98fc..000000000 --- a/exosphere/src/warmboot_init.c +++ /dev/null @@ -1,221 +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 "utils.h" -#include "memory_map.h" -#include "mc.h" -#include "arm.h" -#include "synchronization.h" -#include "exocfg.h" -#include "pmc.h" - -#undef MC_BASE -#define MC_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC)) - -#define WARMBOOT_GET_TZRAM_SEGMENT_PA(x) ((g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) \ - ? TZRAM_GET_SEGMENT_PA(x) : TZRAM_GET_SEGMENT_5X_PA(x)) - -/* start.s */ -void __set_memory_registers(uintptr_t ttbr0, uintptr_t vbar, uint64_t cpuectlr, uint32_t scr, - uint32_t tcr, uint32_t cptr, uint64_t mair, uint32_t sctlr); - -unsigned int g_exosphere_target_firmware_for_init = 0; - -uintptr_t get_warmboot_crt0_stack_address(void) { - return WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x800; -} - -uintptr_t get_warmboot_crt0_stack_address_critsec_enter(void) { - unsigned int core_id = get_core_id(); - - if (core_id == 3) { - return WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x1000; - } else { - return WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x80 * (core_id + 1); - } -} - -void warmboot_crt0_critical_section_enter(volatile critical_section_t *critical_section) { - critical_section_enter(critical_section); -} - -void init_dma_controllers(unsigned int target_firmware) { - if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - /* Set some unknown registers in HOST1X. */ - MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; - MAKE_REG32(0x50003300) = 0; - - /* AHB_MASTER_SWID_0 - Enable SWID[0] for all bits. */ - MAKE_REG32(0x6000C018) = 0xFFFFFFFF; - - /* AHB_MASTER_SWID_1 */ - MAKE_REG32(0x6000C038) = 0x0; - - /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ - MAKE_REG32(0x50060000) = (MAKE_REG32(0x50060000) & 0xC4FFFFFF) | 0x38000000; - - /* AHB_ARBITRATION_DISABLE_0 - Disables USB, USB2, and AHB-DMA from arbitration */ - MAKE_REG32(0x6000C004) = 0x40060; - - /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ - MAKE_REG32(0x6000C008) = 0xE0000001; - - /* AHB_GIZMO_TZRAM_0 |= DONT_SPLIT_AHB_WR */ - MAKE_REG32(0x6000C054) = 0x80; - } else { - /* SYSCTR0_CNTCR_0 = ENABLE | HALT_ON_DEBUG (write-once init) */ - MAKE_REG32(0x700F0000) = 3; - - /* Set some unknown registers in HOST1X. */ - MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; - MAKE_REG32(0x50003300) = 0; - - /* AHB_MASTER_SWID_0 */ - MAKE_REG32(0x6000C018) = 0; - - /* AHB_MASTER_SWID_1 - Makes USB1/USB2 use SWID[1] */ - MAKE_REG32(0x6000C038) = 0x40040; - - /* APBDMA_CHANNEL_SWID_0 = ~0 (SWID = 1 for all APB-DMA channels) */ - MAKE_REG32(0x6002003C) = 0xFFFFFFFF; - - /* APBDMA_CHANNEL_SWID1_0 = 0 (See above) */ - MAKE_REG32(0x60020054) = 0; - - /* APBDMA_SECURITY_REG_0 = 0 (All APB-DMA channels non-secure) */ - MAKE_REG32(0x60020038) = 0; - - /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ - MAKE_REG32(0x50060000) |= (MAKE_REG32(0x50060000) & 0xC4FFFFFF) | 0x38000000; - - /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ - MAKE_REG32(0x6000C008) = 0xE0000001; - - /* AHB_GIZMO_TZRAM_0 |= DONT_SPLIT_AHB_WR */ - MAKE_REG32(0x6000C054) = 0x80; - } -} - -void _set_memory_registers_enable_mmu(const uintptr_t ttbr0) { - static const uintptr_t vbar = TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800; - /* - - Non-cacheable load forwarding enabled - - Disable load-pass DMB. - - NOTE: This and this alone is done via inline asm, due to register argument limits. - */ - - static const uint64_t cpuactlr = 0x800000001000000ull; - __asm__ __volatile__("msr s3_1_c15_c2_0, %0" :: "r"(cpuactlr) : "memory", "cc"); - - /* - - Disable table walk descriptor access prefetch. - - L2 instruction fetch prefetch distance = 3 (reset value) - - L2 load/store data prefetch distance = 8 (reset value) - - Enable the processor to receive instruction cache and TLB maintenance operations broadcast from other processors in the cluster - */ - static const uint64_t cpuectlr = 0x1B00000040ull; - - /* - - The next lower level is Aarch64 - - Secure instruction fetch (when the PE is in Secure state, this bit disables instruction fetch from Non-secure memory) - - External Abort/SError taken to EL3 - - FIQ taken to EL3 - - NS (EL0 and EL1 are nonsecure) - */ - static const uint32_t scr = 0x63D; - - /* - - PA size: 36-bit (64 GB) - - Granule size: 4KB - - Shareability attribute for memory associated with translation table walks using TTBR0_EL3: Inner Shareable - - Outer cacheability attribute for memory associated with translation table walks using TTBR0_EL3: Normal memory, Outer Write-Back Read-Allocate Write-Allocate Cacheable - - Inner cacheability attribute for memory associated with translation table walks using TTBR0_EL3: Normal memory, Inner Write-Back Read-Allocate Write-Allocate Cacheable - - T0SZ = 31 (33-bit address space) - */ - static const uint32_t tcr = TCR_EL3_RSVD | TCR_PS(1) | TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA | TCR_T0SZ(33); - - /* Nothing trapped */ - static const uint32_t cptr = 0; - - /* - - Attribute 0: Normal memory, Inner and Outer Write-Back Read-Allocate Write-Allocate Non-transient - - Attribute 1: Device-nGnRE memory - - Other attributes: Device-nGnRnE memory - */ - static const uint64_t mair = 0x4FFull; - - /* - - Cacheability control, for EL3 instruction accesses DISABLED - (- SP Alignment check bit NOT SET) - - Cacheability control, for EL3 data accesses DISABLED (normal memory accesses from EL3 are cacheable) - (- Alignement check bit NOT SET) - - MMU enabled for EL3 stage 1 address translation - */ - static const uint32_t sctlr = 0x30C51835ull; - - __set_memory_registers(ttbr0, vbar, cpuectlr, scr, tcr, cptr, mair, sctlr); -} - -void set_memory_registers_enable_mmu_1x_ttbr0(void) { - static const uintptr_t ttbr0 = TZRAM_GET_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64; - _set_memory_registers_enable_mmu(ttbr0); -} - -void set_memory_registers_enable_mmu_5x_ttbr0(void) { - static const uintptr_t ttbr0 = TZRAM_GET_SEGMENT_5X_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64; - _set_memory_registers_enable_mmu(ttbr0); -} - -#if 0 /* Since we decided not to identity-unmap TZRAM */ -static void identity_remap_tzram(void) { - /* See also: configure_ttbls (in coldboot_init.c). */ - uintptr_t *mmu_l1_tbl = (uintptr_t *)(WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - uintptr_t *mmu_l2_tbl = (uintptr_t *)WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); - uintptr_t *mmu_l3_tbl = (uintptr_t *)WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - - mmu_map_table(1, mmu_l1_tbl, 0x40000000, mmu_l2_tbl, 0); - mmu_map_table(2, mmu_l2_tbl, 0x7C000000, mmu_l3_tbl, 0); - - identity_map_mapping(mmu_l1_tbl, mmu_l3_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_TZRAM), - IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_TZRAM), IDENTITY_GET_MAPPING_ATTRIBS(IDENTITY_MAPPING_TZRAM), - IDENTITY_IS_MAPPING_BLOCK_RANGE(IDENTITY_MAPPING_TZRAM)); -} -#endif - -void warmboot_init(void) { - /* - From https://events.static.linuxfound.org/sites/events/files/slides/slides_17.pdf : - Caches may write back dirty lines at any time: - - To make space for new allocations - - Even if MMU is off - - Even if Cacheable accesses are disabled (caches are never 'off') - */ - flush_dcache_all(); - invalidate_icache_all(); - - /* On warmboot (not cpu_on) only */ - if (VIRT_MC_SECURITY_CFG3 == 0) { - init_dma_controllers(g_exosphere_target_firmware_for_init); - } - - /*identity_remap_tzram();*/ - /* Nintendo pointlessly fully invalidate the TLB & invalidate the data cache on the modified ranges here */ - if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_5_0_0) { - set_memory_registers_enable_mmu_1x_ttbr0(); - } else { - set_memory_registers_enable_mmu_5x_ttbr0(); - } -} diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c deleted file mode 100644 index d8ee82e91..000000000 --- a/exosphere/src/warmboot_main.c +++ /dev/null @@ -1,108 +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 "utils.h" -#include "mmu.h" -#include "memory_map.h" -#include "cpu_context.h" -#include "bootconfig.h" -#include "configitem.h" -#include "masterkey.h" -#include "bootup.h" -#include "smc_api.h" -#include "exocfg.h" -#include "se.h" -#include "mc.h" -#include "car.h" -#include "i2c.h" -#include "misc.h" -#include "uart.h" -#include "interrupt.h" -#include "pmc.h" - -uintptr_t get_warmboot_main_stack_address(void) { - return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x780; -} - -static void warmboot_configure_hiz_mode(void) { - /* Enable input to I2C1 */ - i2c_config(I2C_1); - - clkrst_reboot(CARDEVICE_I2C1); - i2c_init(0); - i2c_clear_ti_charger_bit_7(); - clkrst_disable(CARDEVICE_I2C1); -} - -void __attribute__((noreturn)) warmboot_main(void) { - /* - This function and its callers are reached in either of the following events, under normal conditions: - - warmboot (core 3) - - cpu_on - */ - - if (is_core_active(get_core_id())) { - panic(0xF7F00009); /* invalid CPU context */ - } - - /* IRAM C+D identity mapping has actually been removed on coldboot but we don't really care */ - /* For our crt0 to work, this doesn't actually unmap TZRAM */ - identity_unmap_iram_cd_tzram(); - - /* On warmboot (not cpu_on) only */ - if (VIRT_MC_SECURITY_CFG3 == 0) { - /* N only does this on dev units, but we will do it unconditionally. */ - { - uart_config(UART_A); - clkrst_reboot(CARDEVICE_UARTA); - uart_init(UART_A, 115200); - } - - if (!configitem_is_retail()) { - uart_send(UART_A, "OHAYO", 6); - uart_wait_idle(UART_A, UART_VENDOR_STATE_TX_IDLE); - } - - /* Sanity check the Security Engine. */ - se_verify_flags_cleared(); - - /* Initialize interrupts. */ - intr_initialize_gic_nonsecure(); - - bootup_misc_mmio(); - - /* Make PMC (2.x+), MC (4.x+) registers secure-only */ - secure_additional_devices(); - - if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) || - ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_8_0_0) && configitem_get_hardware_type() == 0) || - (configitem_is_hiz_mode_enabled())) { - warmboot_configure_hiz_mode(); - } - - clear_user_smc_in_progress(); - - if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - setup_4x_mmio(); - } - } - - setup_current_core_state(); - - /* Update SCR_EL3 depending on value in Bootconfig. */ - set_extabt_serror_taken_to_el3(bootconfig_take_extabt_serror_to_el3()); - core_jump_to_lower_el(); -} From 42f1a3bf60d27ee9b5ecda09ad5bb60ecaf5dd68 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 01:53:10 -0700 Subject: [PATCH 093/118] exo2: rename exosphere2 -> exosphere --- Makefile | 12 ++++++------ {exosphere2 => exosphere}/Makefile | 0 {exosphere2 => exosphere}/loader_stub/Makefile | 0 {exosphere2 => exosphere}/loader_stub/loader_stub.ld | 0 .../loader_stub/loader_stub.specs | 0 .../loader_stub/source/secmon_loader_error.cpp | 0 .../loader_stub/source/secmon_loader_error.hpp | 0 .../loader_stub/source/secmon_loader_main.cpp | 0 .../loader_stub/source/secmon_loader_uncompress.cpp | 0 .../loader_stub/source/secmon_loader_uncompress.hpp | 0 {exosphere2 => exosphere}/loader_stub/source/start.s | 0 {exosphere2 => exosphere}/program/Makefile | 0 {exosphere2 => exosphere}/program/program.ld | 0 {exosphere2 => exosphere}/program/program.specs | 0 .../program/rebootstub/Makefile | 0 .../program/rebootstub/rebootstub.ld | 0 .../program/rebootstub/rebootstub.specs | 0 .../rebootstub/source/rebootstub_exception_vectors.s | 0 .../program/rebootstub/source/rebootstub_main.s | 0 .../rebootstub/source/rebootstub_power_off.cpp | 0 {exosphere2 => exosphere}/program/sc7fw/Makefile | 0 {exosphere2 => exosphere}/program/sc7fw/sc7fw.ld | 0 {exosphere2 => exosphere}/program/sc7fw/sc7fw.specs | 0 .../program/sc7fw/source/sc7fw_dram.cpp | 0 .../program/sc7fw/source/sc7fw_dram.hpp | 0 .../program/sc7fw/source/sc7fw_exception_vectors.s | 0 .../program/sc7fw/source/sc7fw_main.cpp | 0 .../program/sc7fw/source/sc7fw_start.s | 0 .../program/sc7fw/source/sc7fw_util.hpp | 0 .../program/sc7fw/source/sc7fw_util_asm.s | 0 .../program/source/boot/secmon_boot.hpp | 0 .../program/source/boot/secmon_boot_cache.cpp | 0 .../program/source/boot/secmon_boot_cache.hpp | 0 .../program/source/boot/secmon_boot_config.cpp | 0 .../program/source/boot/secmon_boot_functions.cpp | 0 .../program/source/boot/secmon_boot_functions.hpp | 0 .../program/source/boot/secmon_boot_key_data.s | 0 .../program/source/boot/secmon_boot_rsa.cpp | 0 .../program/source/boot/secmon_boot_setup.cpp | 0 .../program/source/boot/secmon_crt0.s | 0 .../program/source/boot/secmon_crt0_cpp.cpp | 0 .../program/source/boot/secmon_main.cpp | 0 .../program/source/boot/secmon_make_page_table.cpp | 0 .../program/source/boot/secmon_package2.cpp | 0 .../program/source/boot/secmon_size_data.s | 0 .../program/source/secmon_cache.cpp | 0 .../program/source/secmon_cache.hpp | 0 .../program/source/secmon_cache.inc | 0 .../program/source/secmon_cache_impl.inc | 0 .../program/source/secmon_cpu_context.cpp | 0 .../program/source/secmon_cpu_context.hpp | 0 .../program/source/secmon_error.cpp | 0 .../program/source/secmon_error.hpp | 0 .../program/source/secmon_exception_vectors.s | 0 .../program/source/secmon_interrupt_handler.cpp | 0 .../program/source/secmon_interrupt_handler.hpp | 0 .../program/source/secmon_key_storage.cpp | 0 .../program/source/secmon_key_storage.hpp | 0 .../program/source/secmon_map.cpp | 0 .../program/source/secmon_map.hpp | 0 .../program/source/secmon_misc.cpp | 0 .../program/source/secmon_misc.hpp | 0 .../program/source/secmon_page_mapper.cpp | 0 .../program/source/secmon_page_mapper.hpp | 0 .../program/source/secmon_setup.cpp | 0 .../program/source/secmon_setup.hpp | 0 .../program/source/secmon_setup_warm.cpp | 0 .../program/source/secmon_spinlock.hpp | 0 .../program/source/secmon_spinlock.s | 0 .../program/source/secmon_stack_warm.s | 0 .../program/source/secmon_start_virtual.s | 0 .../program/source/secmon_start_warm.s | 0 .../program/source/secmon_user_power_management.cpp | 0 .../program/source/secmon_user_power_management.hpp | 0 .../source/smc/secmon_define_access_table.inc | 0 .../source/smc/secmon_define_mc01_access_table.inc | 0 .../source/smc/secmon_define_mc_access_table.inc | 0 .../source/smc/secmon_define_pmc_access_table.inc | 0 .../source/smc/secmon_mc01_access_table_data.inc | 0 .../source/smc/secmon_mc_access_table_data.inc | 0 .../source/smc/secmon_pmc_access_table_data.inc | 0 .../program/source/smc/secmon_random_cache.cpp | 0 .../program/source/smc/secmon_random_cache.hpp | 0 .../program/source/smc/secmon_smc_aes.cpp | 0 .../program/source/smc/secmon_smc_aes.hpp | 0 .../program/source/smc/secmon_smc_carveout.cpp | 0 .../program/source/smc/secmon_smc_carveout.hpp | 0 .../program/source/smc/secmon_smc_common.hpp | 0 .../program/source/smc/secmon_smc_cpu_asm.s | 0 .../source/smc/secmon_smc_device_unique_data.cpp | 0 .../source/smc/secmon_smc_device_unique_data.hpp | 0 .../program/source/smc/secmon_smc_error.cpp | 0 .../program/source/smc/secmon_smc_error.hpp | 0 .../program/source/smc/secmon_smc_handler.cpp | 0 .../program/source/smc/secmon_smc_handler.hpp | 0 .../program/source/smc/secmon_smc_info.cpp | 0 .../program/source/smc/secmon_smc_info.hpp | 0 .../program/source/smc/secmon_smc_memory_access.cpp | 0 .../program/source/smc/secmon_smc_memory_access.hpp | 0 .../source/smc/secmon_smc_power_management.cpp | 0 .../source/smc/secmon_smc_power_management.hpp | 0 .../program/source/smc/secmon_smc_random.cpp | 0 .../program/source/smc/secmon_smc_random.hpp | 0 .../source/smc/secmon_smc_register_access.cpp | 0 .../source/smc/secmon_smc_register_access.hpp | 0 .../program/source/smc/secmon_smc_result.cpp | 0 .../program/source/smc/secmon_smc_result.hpp | 0 .../program/source/smc/secmon_smc_rsa.cpp | 0 .../program/source/smc/secmon_smc_rsa.hpp | 0 .../program/source/smc/secmon_smc_se_lock.cpp | 0 .../program/source/smc/secmon_smc_se_lock.hpp | 0 {exosphere2 => exosphere}/program/split_program.py | 0 {exosphere2 => exosphere}/warmboot/Makefile | 0 .../warmboot/source/warmboot_bootrom_workaround.cpp | 0 .../warmboot/source/warmboot_bootrom_workaround.hpp | 0 .../warmboot/source/warmboot_clkrst.cpp | 0 .../warmboot/source/warmboot_clkrst.hpp | 0 .../warmboot/source/warmboot_cpu_cluster.cpp | 0 .../warmboot/source/warmboot_cpu_cluster.hpp | 0 .../warmboot/source/warmboot_dram.cpp | 0 .../warmboot/source/warmboot_dram.hpp | 0 .../warmboot/source/warmboot_exception_vectors.s | 0 .../warmboot/source/warmboot_main.cpp | 0 .../warmboot/source/warmboot_main.hpp | 0 .../warmboot/source/warmboot_misc.cpp | 0 .../warmboot/source/warmboot_misc.hpp | 0 .../warmboot/source/warmboot_secure_monitor.cpp | 0 .../warmboot/source/warmboot_secure_monitor.hpp | 0 .../warmboot/source/warmboot_start.s | 0 .../warmboot/source/warmboot_util.hpp | 0 .../warmboot/source/warmboot_util_asm.s | 0 {exosphere2 => exosphere}/warmboot/warmboot.ld | 0 {exosphere2 => exosphere}/warmboot/warmboot.specs | 0 fusee/fusee-primary/Makefile | 6 +++--- fusee/fusee-secondary/Makefile | 6 +++--- sept/sept-secondary/Makefile | 6 +++--- 136 files changed, 15 insertions(+), 15 deletions(-) rename {exosphere2 => exosphere}/Makefile (100%) rename {exosphere2 => exosphere}/loader_stub/Makefile (100%) rename {exosphere2 => exosphere}/loader_stub/loader_stub.ld (100%) rename {exosphere2 => exosphere}/loader_stub/loader_stub.specs (100%) rename {exosphere2 => exosphere}/loader_stub/source/secmon_loader_error.cpp (100%) rename {exosphere2 => exosphere}/loader_stub/source/secmon_loader_error.hpp (100%) rename {exosphere2 => exosphere}/loader_stub/source/secmon_loader_main.cpp (100%) rename {exosphere2 => exosphere}/loader_stub/source/secmon_loader_uncompress.cpp (100%) rename {exosphere2 => exosphere}/loader_stub/source/secmon_loader_uncompress.hpp (100%) rename {exosphere2 => exosphere}/loader_stub/source/start.s (100%) rename {exosphere2 => exosphere}/program/Makefile (100%) rename {exosphere2 => exosphere}/program/program.ld (100%) rename {exosphere2 => exosphere}/program/program.specs (100%) rename {exosphere2 => exosphere}/program/rebootstub/Makefile (100%) rename {exosphere2 => exosphere}/program/rebootstub/rebootstub.ld (100%) rename {exosphere2 => exosphere}/program/rebootstub/rebootstub.specs (100%) rename {exosphere2 => exosphere}/program/rebootstub/source/rebootstub_exception_vectors.s (100%) rename {exosphere2 => exosphere}/program/rebootstub/source/rebootstub_main.s (100%) rename {exosphere2 => exosphere}/program/rebootstub/source/rebootstub_power_off.cpp (100%) rename {exosphere2 => exosphere}/program/sc7fw/Makefile (100%) rename {exosphere2 => exosphere}/program/sc7fw/sc7fw.ld (100%) rename {exosphere2 => exosphere}/program/sc7fw/sc7fw.specs (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_dram.cpp (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_dram.hpp (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_exception_vectors.s (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_main.cpp (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_start.s (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_util.hpp (100%) rename {exosphere2 => exosphere}/program/sc7fw/source/sc7fw_util_asm.s (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot.hpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_cache.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_cache.hpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_config.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_functions.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_functions.hpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_key_data.s (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_rsa.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_boot_setup.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_crt0.s (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_crt0_cpp.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_main.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_make_page_table.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_package2.cpp (100%) rename {exosphere2 => exosphere}/program/source/boot/secmon_size_data.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_cache.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_cache.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_cache.inc (100%) rename {exosphere2 => exosphere}/program/source/secmon_cache_impl.inc (100%) rename {exosphere2 => exosphere}/program/source/secmon_cpu_context.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_cpu_context.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_error.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_error.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_exception_vectors.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_interrupt_handler.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_interrupt_handler.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_key_storage.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_key_storage.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_map.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_map.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_misc.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_misc.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_page_mapper.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_page_mapper.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_setup.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_setup.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_setup_warm.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_spinlock.hpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_spinlock.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_stack_warm.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_start_virtual.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_start_warm.s (100%) rename {exosphere2 => exosphere}/program/source/secmon_user_power_management.cpp (100%) rename {exosphere2 => exosphere}/program/source/secmon_user_power_management.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_define_access_table.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_define_mc01_access_table.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_define_mc_access_table.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_define_pmc_access_table.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_mc01_access_table_data.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_mc_access_table_data.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_pmc_access_table_data.inc (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_random_cache.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_random_cache.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_aes.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_aes.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_carveout.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_carveout.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_common.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_cpu_asm.s (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_device_unique_data.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_device_unique_data.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_error.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_error.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_handler.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_handler.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_info.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_info.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_memory_access.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_memory_access.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_power_management.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_power_management.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_random.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_random.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_register_access.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_register_access.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_result.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_result.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_rsa.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_rsa.hpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_se_lock.cpp (100%) rename {exosphere2 => exosphere}/program/source/smc/secmon_smc_se_lock.hpp (100%) rename {exosphere2 => exosphere}/program/split_program.py (100%) rename {exosphere2 => exosphere}/warmboot/Makefile (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_bootrom_workaround.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_bootrom_workaround.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_clkrst.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_clkrst.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_cpu_cluster.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_cpu_cluster.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_dram.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_dram.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_exception_vectors.s (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_main.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_main.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_misc.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_misc.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_secure_monitor.cpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_secure_monitor.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_start.s (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_util.hpp (100%) rename {exosphere2 => exosphere}/warmboot/source/warmboot_util_asm.s (100%) rename {exosphere2 => exosphere}/warmboot/warmboot.ld (100%) rename {exosphere2 => exosphere}/warmboot/warmboot.specs (100%) diff --git a/Makefile b/Makefile index 2e3302b9d..7fbc96576 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ thermosphere: $(MAKE) -C thermosphere all exosphere: thermosphere - $(MAKE) -C exosphere2 all + $(MAKE) -C exosphere all stratosphere: exosphere libraries $(MAKE) -C stratosphere all @@ -120,11 +120,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 exosphere2/loader_stub/loader_stub.elf atmosphere-$(AMSVER)-debug/exosphere-loader-stub.elf - cp exosphere2/program/program.elf atmosphere-$(AMSVER)-debug/exosphere-program.elf - cp exosphere2/warmboot/warmboot.elf atmosphere-$(AMSVER)-debug/exosphere-warmboot.elf - cp exosphere2/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf - cp exosphere2/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-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 diff --git a/exosphere2/Makefile b/exosphere/Makefile similarity index 100% rename from exosphere2/Makefile rename to exosphere/Makefile diff --git a/exosphere2/loader_stub/Makefile b/exosphere/loader_stub/Makefile similarity index 100% rename from exosphere2/loader_stub/Makefile rename to exosphere/loader_stub/Makefile diff --git a/exosphere2/loader_stub/loader_stub.ld b/exosphere/loader_stub/loader_stub.ld similarity index 100% rename from exosphere2/loader_stub/loader_stub.ld rename to exosphere/loader_stub/loader_stub.ld diff --git a/exosphere2/loader_stub/loader_stub.specs b/exosphere/loader_stub/loader_stub.specs similarity index 100% rename from exosphere2/loader_stub/loader_stub.specs rename to exosphere/loader_stub/loader_stub.specs diff --git a/exosphere2/loader_stub/source/secmon_loader_error.cpp b/exosphere/loader_stub/source/secmon_loader_error.cpp similarity index 100% rename from exosphere2/loader_stub/source/secmon_loader_error.cpp rename to exosphere/loader_stub/source/secmon_loader_error.cpp diff --git a/exosphere2/loader_stub/source/secmon_loader_error.hpp b/exosphere/loader_stub/source/secmon_loader_error.hpp similarity index 100% rename from exosphere2/loader_stub/source/secmon_loader_error.hpp rename to exosphere/loader_stub/source/secmon_loader_error.hpp diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere/loader_stub/source/secmon_loader_main.cpp similarity index 100% rename from exosphere2/loader_stub/source/secmon_loader_main.cpp rename to exosphere/loader_stub/source/secmon_loader_main.cpp diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere/loader_stub/source/secmon_loader_uncompress.cpp similarity index 100% rename from exosphere2/loader_stub/source/secmon_loader_uncompress.cpp rename to exosphere/loader_stub/source/secmon_loader_uncompress.cpp diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere/loader_stub/source/secmon_loader_uncompress.hpp similarity index 100% rename from exosphere2/loader_stub/source/secmon_loader_uncompress.hpp rename to exosphere/loader_stub/source/secmon_loader_uncompress.hpp diff --git a/exosphere2/loader_stub/source/start.s b/exosphere/loader_stub/source/start.s similarity index 100% rename from exosphere2/loader_stub/source/start.s rename to exosphere/loader_stub/source/start.s diff --git a/exosphere2/program/Makefile b/exosphere/program/Makefile similarity index 100% rename from exosphere2/program/Makefile rename to exosphere/program/Makefile diff --git a/exosphere2/program/program.ld b/exosphere/program/program.ld similarity index 100% rename from exosphere2/program/program.ld rename to exosphere/program/program.ld diff --git a/exosphere2/program/program.specs b/exosphere/program/program.specs similarity index 100% rename from exosphere2/program/program.specs rename to exosphere/program/program.specs diff --git a/exosphere2/program/rebootstub/Makefile b/exosphere/program/rebootstub/Makefile similarity index 100% rename from exosphere2/program/rebootstub/Makefile rename to exosphere/program/rebootstub/Makefile diff --git a/exosphere2/program/rebootstub/rebootstub.ld b/exosphere/program/rebootstub/rebootstub.ld similarity index 100% rename from exosphere2/program/rebootstub/rebootstub.ld rename to exosphere/program/rebootstub/rebootstub.ld diff --git a/exosphere2/program/rebootstub/rebootstub.specs b/exosphere/program/rebootstub/rebootstub.specs similarity index 100% rename from exosphere2/program/rebootstub/rebootstub.specs rename to exosphere/program/rebootstub/rebootstub.specs diff --git a/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s b/exosphere/program/rebootstub/source/rebootstub_exception_vectors.s similarity index 100% rename from exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s rename to exosphere/program/rebootstub/source/rebootstub_exception_vectors.s diff --git a/exosphere2/program/rebootstub/source/rebootstub_main.s b/exosphere/program/rebootstub/source/rebootstub_main.s similarity index 100% rename from exosphere2/program/rebootstub/source/rebootstub_main.s rename to exosphere/program/rebootstub/source/rebootstub_main.s diff --git a/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp similarity index 100% rename from exosphere2/program/rebootstub/source/rebootstub_power_off.cpp rename to exosphere/program/rebootstub/source/rebootstub_power_off.cpp diff --git a/exosphere2/program/sc7fw/Makefile b/exosphere/program/sc7fw/Makefile similarity index 100% rename from exosphere2/program/sc7fw/Makefile rename to exosphere/program/sc7fw/Makefile diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere/program/sc7fw/sc7fw.ld similarity index 100% rename from exosphere2/program/sc7fw/sc7fw.ld rename to exosphere/program/sc7fw/sc7fw.ld diff --git a/exosphere2/program/sc7fw/sc7fw.specs b/exosphere/program/sc7fw/sc7fw.specs similarity index 100% rename from exosphere2/program/sc7fw/sc7fw.specs rename to exosphere/program/sc7fw/sc7fw.specs diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere/program/sc7fw/source/sc7fw_dram.cpp similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_dram.cpp rename to exosphere/program/sc7fw/source/sc7fw_dram.cpp diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.hpp b/exosphere/program/sc7fw/source/sc7fw_dram.hpp similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_dram.hpp rename to exosphere/program/sc7fw/source/sc7fw_dram.hpp diff --git a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere/program/sc7fw/source/sc7fw_exception_vectors.s similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s rename to exosphere/program/sc7fw/source/sc7fw_exception_vectors.s diff --git a/exosphere2/program/sc7fw/source/sc7fw_main.cpp b/exosphere/program/sc7fw/source/sc7fw_main.cpp similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_main.cpp rename to exosphere/program/sc7fw/source/sc7fw_main.cpp diff --git a/exosphere2/program/sc7fw/source/sc7fw_start.s b/exosphere/program/sc7fw/source/sc7fw_start.s similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_start.s rename to exosphere/program/sc7fw/source/sc7fw_start.s diff --git a/exosphere2/program/sc7fw/source/sc7fw_util.hpp b/exosphere/program/sc7fw/source/sc7fw_util.hpp similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_util.hpp rename to exosphere/program/sc7fw/source/sc7fw_util.hpp diff --git a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s b/exosphere/program/sc7fw/source/sc7fw_util_asm.s similarity index 100% rename from exosphere2/program/sc7fw/source/sc7fw_util_asm.s rename to exosphere/program/sc7fw/source/sc7fw_util_asm.s diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere/program/source/boot/secmon_boot.hpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot.hpp rename to exosphere/program/source/boot/secmon_boot.hpp diff --git a/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere/program/source/boot/secmon_boot_cache.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_cache.cpp rename to exosphere/program/source/boot/secmon_boot_cache.cpp diff --git a/exosphere2/program/source/boot/secmon_boot_cache.hpp b/exosphere/program/source/boot/secmon_boot_cache.hpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_cache.hpp rename to exosphere/program/source/boot/secmon_boot_cache.hpp diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere/program/source/boot/secmon_boot_config.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_config.cpp rename to exosphere/program/source/boot/secmon_boot_config.cpp diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere/program/source/boot/secmon_boot_functions.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_functions.cpp rename to exosphere/program/source/boot/secmon_boot_functions.cpp diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere/program/source/boot/secmon_boot_functions.hpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_functions.hpp rename to exosphere/program/source/boot/secmon_boot_functions.hpp diff --git a/exosphere2/program/source/boot/secmon_boot_key_data.s b/exosphere/program/source/boot/secmon_boot_key_data.s similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_key_data.s rename to exosphere/program/source/boot/secmon_boot_key_data.s diff --git a/exosphere2/program/source/boot/secmon_boot_rsa.cpp b/exosphere/program/source/boot/secmon_boot_rsa.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_rsa.cpp rename to exosphere/program/source/boot/secmon_boot_rsa.cpp diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere/program/source/boot/secmon_boot_setup.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_boot_setup.cpp rename to exosphere/program/source/boot/secmon_boot_setup.cpp diff --git a/exosphere2/program/source/boot/secmon_crt0.s b/exosphere/program/source/boot/secmon_crt0.s similarity index 100% rename from exosphere2/program/source/boot/secmon_crt0.s rename to exosphere/program/source/boot/secmon_crt0.s diff --git a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp b/exosphere/program/source/boot/secmon_crt0_cpp.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_crt0_cpp.cpp rename to exosphere/program/source/boot/secmon_crt0_cpp.cpp diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere/program/source/boot/secmon_main.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_main.cpp rename to exosphere/program/source/boot/secmon_main.cpp diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere/program/source/boot/secmon_make_page_table.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_make_page_table.cpp rename to exosphere/program/source/boot/secmon_make_page_table.cpp diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere/program/source/boot/secmon_package2.cpp similarity index 100% rename from exosphere2/program/source/boot/secmon_package2.cpp rename to exosphere/program/source/boot/secmon_package2.cpp diff --git a/exosphere2/program/source/boot/secmon_size_data.s b/exosphere/program/source/boot/secmon_size_data.s similarity index 100% rename from exosphere2/program/source/boot/secmon_size_data.s rename to exosphere/program/source/boot/secmon_size_data.s diff --git a/exosphere2/program/source/secmon_cache.cpp b/exosphere/program/source/secmon_cache.cpp similarity index 100% rename from exosphere2/program/source/secmon_cache.cpp rename to exosphere/program/source/secmon_cache.cpp diff --git a/exosphere2/program/source/secmon_cache.hpp b/exosphere/program/source/secmon_cache.hpp similarity index 100% rename from exosphere2/program/source/secmon_cache.hpp rename to exosphere/program/source/secmon_cache.hpp diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere/program/source/secmon_cache.inc similarity index 100% rename from exosphere2/program/source/secmon_cache.inc rename to exosphere/program/source/secmon_cache.inc diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere/program/source/secmon_cache_impl.inc similarity index 100% rename from exosphere2/program/source/secmon_cache_impl.inc rename to exosphere/program/source/secmon_cache_impl.inc diff --git a/exosphere2/program/source/secmon_cpu_context.cpp b/exosphere/program/source/secmon_cpu_context.cpp similarity index 100% rename from exosphere2/program/source/secmon_cpu_context.cpp rename to exosphere/program/source/secmon_cpu_context.cpp diff --git a/exosphere2/program/source/secmon_cpu_context.hpp b/exosphere/program/source/secmon_cpu_context.hpp similarity index 100% rename from exosphere2/program/source/secmon_cpu_context.hpp rename to exosphere/program/source/secmon_cpu_context.hpp diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp similarity index 100% rename from exosphere2/program/source/secmon_error.cpp rename to exosphere/program/source/secmon_error.cpp diff --git a/exosphere2/program/source/secmon_error.hpp b/exosphere/program/source/secmon_error.hpp similarity index 100% rename from exosphere2/program/source/secmon_error.hpp rename to exosphere/program/source/secmon_error.hpp diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere/program/source/secmon_exception_vectors.s similarity index 100% rename from exosphere2/program/source/secmon_exception_vectors.s rename to exosphere/program/source/secmon_exception_vectors.s diff --git a/exosphere2/program/source/secmon_interrupt_handler.cpp b/exosphere/program/source/secmon_interrupt_handler.cpp similarity index 100% rename from exosphere2/program/source/secmon_interrupt_handler.cpp rename to exosphere/program/source/secmon_interrupt_handler.cpp diff --git a/exosphere2/program/source/secmon_interrupt_handler.hpp b/exosphere/program/source/secmon_interrupt_handler.hpp similarity index 100% rename from exosphere2/program/source/secmon_interrupt_handler.hpp rename to exosphere/program/source/secmon_interrupt_handler.hpp diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere/program/source/secmon_key_storage.cpp similarity index 100% rename from exosphere2/program/source/secmon_key_storage.cpp rename to exosphere/program/source/secmon_key_storage.cpp diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere/program/source/secmon_key_storage.hpp similarity index 100% rename from exosphere2/program/source/secmon_key_storage.hpp rename to exosphere/program/source/secmon_key_storage.hpp diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere/program/source/secmon_map.cpp similarity index 100% rename from exosphere2/program/source/secmon_map.cpp rename to exosphere/program/source/secmon_map.cpp diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere/program/source/secmon_map.hpp similarity index 100% rename from exosphere2/program/source/secmon_map.hpp rename to exosphere/program/source/secmon_map.hpp diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere/program/source/secmon_misc.cpp similarity index 100% rename from exosphere2/program/source/secmon_misc.cpp rename to exosphere/program/source/secmon_misc.cpp diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere/program/source/secmon_misc.hpp similarity index 100% rename from exosphere2/program/source/secmon_misc.hpp rename to exosphere/program/source/secmon_misc.hpp diff --git a/exosphere2/program/source/secmon_page_mapper.cpp b/exosphere/program/source/secmon_page_mapper.cpp similarity index 100% rename from exosphere2/program/source/secmon_page_mapper.cpp rename to exosphere/program/source/secmon_page_mapper.cpp diff --git a/exosphere2/program/source/secmon_page_mapper.hpp b/exosphere/program/source/secmon_page_mapper.hpp similarity index 100% rename from exosphere2/program/source/secmon_page_mapper.hpp rename to exosphere/program/source/secmon_page_mapper.hpp diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp similarity index 100% rename from exosphere2/program/source/secmon_setup.cpp rename to exosphere/program/source/secmon_setup.cpp diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere/program/source/secmon_setup.hpp similarity index 100% rename from exosphere2/program/source/secmon_setup.hpp rename to exosphere/program/source/secmon_setup.hpp diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere/program/source/secmon_setup_warm.cpp similarity index 100% rename from exosphere2/program/source/secmon_setup_warm.cpp rename to exosphere/program/source/secmon_setup_warm.cpp diff --git a/exosphere2/program/source/secmon_spinlock.hpp b/exosphere/program/source/secmon_spinlock.hpp similarity index 100% rename from exosphere2/program/source/secmon_spinlock.hpp rename to exosphere/program/source/secmon_spinlock.hpp diff --git a/exosphere2/program/source/secmon_spinlock.s b/exosphere/program/source/secmon_spinlock.s similarity index 100% rename from exosphere2/program/source/secmon_spinlock.s rename to exosphere/program/source/secmon_spinlock.s diff --git a/exosphere2/program/source/secmon_stack_warm.s b/exosphere/program/source/secmon_stack_warm.s similarity index 100% rename from exosphere2/program/source/secmon_stack_warm.s rename to exosphere/program/source/secmon_stack_warm.s diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere/program/source/secmon_start_virtual.s similarity index 100% rename from exosphere2/program/source/secmon_start_virtual.s rename to exosphere/program/source/secmon_start_virtual.s diff --git a/exosphere2/program/source/secmon_start_warm.s b/exosphere/program/source/secmon_start_warm.s similarity index 100% rename from exosphere2/program/source/secmon_start_warm.s rename to exosphere/program/source/secmon_start_warm.s diff --git a/exosphere2/program/source/secmon_user_power_management.cpp b/exosphere/program/source/secmon_user_power_management.cpp similarity index 100% rename from exosphere2/program/source/secmon_user_power_management.cpp rename to exosphere/program/source/secmon_user_power_management.cpp diff --git a/exosphere2/program/source/secmon_user_power_management.hpp b/exosphere/program/source/secmon_user_power_management.hpp similarity index 100% rename from exosphere2/program/source/secmon_user_power_management.hpp rename to exosphere/program/source/secmon_user_power_management.hpp diff --git a/exosphere2/program/source/smc/secmon_define_access_table.inc b/exosphere/program/source/smc/secmon_define_access_table.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_define_access_table.inc rename to exosphere/program/source/smc/secmon_define_access_table.inc diff --git a/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc b/exosphere/program/source/smc/secmon_define_mc01_access_table.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_define_mc01_access_table.inc rename to exosphere/program/source/smc/secmon_define_mc01_access_table.inc diff --git a/exosphere2/program/source/smc/secmon_define_mc_access_table.inc b/exosphere/program/source/smc/secmon_define_mc_access_table.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_define_mc_access_table.inc rename to exosphere/program/source/smc/secmon_define_mc_access_table.inc diff --git a/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc b/exosphere/program/source/smc/secmon_define_pmc_access_table.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_define_pmc_access_table.inc rename to exosphere/program/source/smc/secmon_define_pmc_access_table.inc diff --git a/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc b/exosphere/program/source/smc/secmon_mc01_access_table_data.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_mc01_access_table_data.inc rename to exosphere/program/source/smc/secmon_mc01_access_table_data.inc diff --git a/exosphere2/program/source/smc/secmon_mc_access_table_data.inc b/exosphere/program/source/smc/secmon_mc_access_table_data.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_mc_access_table_data.inc rename to exosphere/program/source/smc/secmon_mc_access_table_data.inc diff --git a/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc b/exosphere/program/source/smc/secmon_pmc_access_table_data.inc similarity index 100% rename from exosphere2/program/source/smc/secmon_pmc_access_table_data.inc rename to exosphere/program/source/smc/secmon_pmc_access_table_data.inc diff --git a/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere/program/source/smc/secmon_random_cache.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_random_cache.cpp rename to exosphere/program/source/smc/secmon_random_cache.cpp diff --git a/exosphere2/program/source/smc/secmon_random_cache.hpp b/exosphere/program/source/smc/secmon_random_cache.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_random_cache.hpp rename to exosphere/program/source/smc/secmon_random_cache.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_aes.cpp rename to exosphere/program/source/smc/secmon_smc_aes.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere/program/source/smc/secmon_smc_aes.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_aes.hpp rename to exosphere/program/source/smc/secmon_smc_aes.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere/program/source/smc/secmon_smc_carveout.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_carveout.cpp rename to exosphere/program/source/smc/secmon_smc_carveout.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere/program/source/smc/secmon_smc_carveout.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_carveout.hpp rename to exosphere/program/source/smc/secmon_smc_carveout.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere/program/source/smc/secmon_smc_common.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_common.hpp rename to exosphere/program/source/smc/secmon_smc_common.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_cpu_asm.s b/exosphere/program/source/smc/secmon_smc_cpu_asm.s similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_cpu_asm.s rename to exosphere/program/source/smc/secmon_smc_cpu_asm.s diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp rename to exosphere/program/source/smc/secmon_smc_device_unique_data.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp rename to exosphere/program/source/smc/secmon_smc_device_unique_data.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere/program/source/smc/secmon_smc_error.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_error.cpp rename to exosphere/program/source/smc/secmon_smc_error.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere/program/source/smc/secmon_smc_error.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_error.hpp rename to exosphere/program/source/smc/secmon_smc_error.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_handler.cpp rename to exosphere/program/source/smc/secmon_smc_handler.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere/program/source/smc/secmon_smc_handler.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_handler.hpp rename to exosphere/program/source/smc/secmon_smc_handler.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_info.cpp rename to exosphere/program/source/smc/secmon_smc_info.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_info.hpp rename to exosphere/program/source/smc/secmon_smc_info.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere/program/source/smc/secmon_smc_memory_access.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_memory_access.cpp rename to exosphere/program/source/smc/secmon_smc_memory_access.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere/program/source/smc/secmon_smc_memory_access.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_memory_access.hpp rename to exosphere/program/source/smc/secmon_smc_memory_access.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_power_management.cpp rename to exosphere/program/source/smc/secmon_smc_power_management.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere/program/source/smc/secmon_smc_power_management.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_power_management.hpp rename to exosphere/program/source/smc/secmon_smc_power_management.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere/program/source/smc/secmon_smc_random.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_random.cpp rename to exosphere/program/source/smc/secmon_smc_random.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere/program/source/smc/secmon_smc_random.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_random.hpp rename to exosphere/program/source/smc/secmon_smc_random.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere/program/source/smc/secmon_smc_register_access.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_register_access.cpp rename to exosphere/program/source/smc/secmon_smc_register_access.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere/program/source/smc/secmon_smc_register_access.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_register_access.hpp rename to exosphere/program/source/smc/secmon_smc_register_access.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere/program/source/smc/secmon_smc_result.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_result.cpp rename to exosphere/program/source/smc/secmon_smc_result.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere/program/source/smc/secmon_smc_result.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_result.hpp rename to exosphere/program/source/smc/secmon_smc_result.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere/program/source/smc/secmon_smc_rsa.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_rsa.cpp rename to exosphere/program/source/smc/secmon_smc_rsa.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere/program/source/smc/secmon_smc_rsa.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_rsa.hpp rename to exosphere/program/source/smc/secmon_smc_rsa.hpp diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere/program/source/smc/secmon_smc_se_lock.cpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_se_lock.cpp rename to exosphere/program/source/smc/secmon_smc_se_lock.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp b/exosphere/program/source/smc/secmon_smc_se_lock.hpp similarity index 100% rename from exosphere2/program/source/smc/secmon_smc_se_lock.hpp rename to exosphere/program/source/smc/secmon_smc_se_lock.hpp diff --git a/exosphere2/program/split_program.py b/exosphere/program/split_program.py similarity index 100% rename from exosphere2/program/split_program.py rename to exosphere/program/split_program.py diff --git a/exosphere2/warmboot/Makefile b/exosphere/warmboot/Makefile similarity index 100% rename from exosphere2/warmboot/Makefile rename to exosphere/warmboot/Makefile diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp b/exosphere/warmboot/source/warmboot_bootrom_workaround.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp rename to exosphere/warmboot/source/warmboot_bootrom_workaround.cpp diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp b/exosphere/warmboot/source/warmboot_bootrom_workaround.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp rename to exosphere/warmboot/source/warmboot_bootrom_workaround.hpp diff --git a/exosphere2/warmboot/source/warmboot_clkrst.cpp b/exosphere/warmboot/source/warmboot_clkrst.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_clkrst.cpp rename to exosphere/warmboot/source/warmboot_clkrst.cpp diff --git a/exosphere2/warmboot/source/warmboot_clkrst.hpp b/exosphere/warmboot/source/warmboot_clkrst.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_clkrst.hpp rename to exosphere/warmboot/source/warmboot_clkrst.hpp diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp b/exosphere/warmboot/source/warmboot_cpu_cluster.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_cpu_cluster.cpp rename to exosphere/warmboot/source/warmboot_cpu_cluster.cpp diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp b/exosphere/warmboot/source/warmboot_cpu_cluster.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_cpu_cluster.hpp rename to exosphere/warmboot/source/warmboot_cpu_cluster.hpp diff --git a/exosphere2/warmboot/source/warmboot_dram.cpp b/exosphere/warmboot/source/warmboot_dram.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_dram.cpp rename to exosphere/warmboot/source/warmboot_dram.cpp diff --git a/exosphere2/warmboot/source/warmboot_dram.hpp b/exosphere/warmboot/source/warmboot_dram.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_dram.hpp rename to exosphere/warmboot/source/warmboot_dram.hpp diff --git a/exosphere2/warmboot/source/warmboot_exception_vectors.s b/exosphere/warmboot/source/warmboot_exception_vectors.s similarity index 100% rename from exosphere2/warmboot/source/warmboot_exception_vectors.s rename to exosphere/warmboot/source/warmboot_exception_vectors.s diff --git a/exosphere2/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_main.cpp rename to exosphere/warmboot/source/warmboot_main.cpp diff --git a/exosphere2/warmboot/source/warmboot_main.hpp b/exosphere/warmboot/source/warmboot_main.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_main.hpp rename to exosphere/warmboot/source/warmboot_main.hpp diff --git a/exosphere2/warmboot/source/warmboot_misc.cpp b/exosphere/warmboot/source/warmboot_misc.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_misc.cpp rename to exosphere/warmboot/source/warmboot_misc.cpp diff --git a/exosphere2/warmboot/source/warmboot_misc.hpp b/exosphere/warmboot/source/warmboot_misc.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_misc.hpp rename to exosphere/warmboot/source/warmboot_misc.hpp diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.cpp b/exosphere/warmboot/source/warmboot_secure_monitor.cpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_secure_monitor.cpp rename to exosphere/warmboot/source/warmboot_secure_monitor.cpp diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.hpp b/exosphere/warmboot/source/warmboot_secure_monitor.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_secure_monitor.hpp rename to exosphere/warmboot/source/warmboot_secure_monitor.hpp diff --git a/exosphere2/warmboot/source/warmboot_start.s b/exosphere/warmboot/source/warmboot_start.s similarity index 100% rename from exosphere2/warmboot/source/warmboot_start.s rename to exosphere/warmboot/source/warmboot_start.s diff --git a/exosphere2/warmboot/source/warmboot_util.hpp b/exosphere/warmboot/source/warmboot_util.hpp similarity index 100% rename from exosphere2/warmboot/source/warmboot_util.hpp rename to exosphere/warmboot/source/warmboot_util.hpp diff --git a/exosphere2/warmboot/source/warmboot_util_asm.s b/exosphere/warmboot/source/warmboot_util_asm.s similarity index 100% rename from exosphere2/warmboot/source/warmboot_util_asm.s rename to exosphere/warmboot/source/warmboot_util_asm.s diff --git a/exosphere2/warmboot/warmboot.ld b/exosphere/warmboot/warmboot.ld similarity index 100% rename from exosphere2/warmboot/warmboot.ld rename to exosphere/warmboot/warmboot.ld diff --git a/exosphere2/warmboot/warmboot.specs b/exosphere/warmboot/warmboot.specs similarity index 100% rename from exosphere2/warmboot/warmboot.specs rename to exosphere/warmboot/warmboot.specs diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index dd3828792..c471da0d4 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -77,7 +77,7 @@ export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2/program/rebootstub + $(AMS)/exosphere/program/rebootstub export DEPSDIR := $(CURDIR)/$(BUILD) @@ -117,7 +117,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) all: check_rebootstub $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub all $(BUILD): check_rebootstub @[ -d $@ ] || mkdir -p $@ @@ -126,7 +126,7 @@ $(BUILD): check_rebootstub #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index b2798e1a1..51a408be4 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -87,7 +87,7 @@ export TOPDIR := $(CURDIR) export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2 $(AMS)/exosphere2/warmboot $(AMS)/exosphere2/program/rebootstub \ + $(AMS)/exosphere $(AMS)/exosphere/warmboot $(AMS)/exosphere/program/rebootstub \ $(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \ $(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere/kernel_ldr $(KIPDIRS) @@ -137,7 +137,7 @@ check_fusee_primary: @$(MAKE) -C $(AMS)/fusee/fusee-primary all check_exosphere: - @$(MAKE) -C $(AMS)/exosphere2 all + @$(MAKE) -C $(AMS)/exosphere all check_sept: @$(MAKE) -C $(AMS)/sept all @@ -166,7 +166,7 @@ $(BUILD): check_fusee_primary check_exosphere check_sept check_emummc check_ther clean: @echo clean ... @$(MAKE) -C $(AMS)/fusee/fusee-primary clean - @$(MAKE) -C $(AMS)/exosphere2 clean + @$(MAKE) -C $(AMS)/exosphere clean @$(MAKE) -C $(AMS)/thermosphere clean @$(MAKE) -C $(AMS)/libraries clean @$(MAKE) -C $(AMS)/mesosphere clean diff --git a/sept/sept-secondary/Makefile b/sept/sept-secondary/Makefile index e8567f0dd..93624f79e 100644 --- a/sept/sept-secondary/Makefile +++ b/sept/sept-secondary/Makefile @@ -77,7 +77,7 @@ export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2/program/rebootstub \ + $(AMS)/exosphere/program/rebootstub \ $(TOPDIR)/key_derivation export DEPSDIR := $(CURDIR)/$(BUILD) @@ -118,7 +118,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) all: check_rebootstub check_key_derivation $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub all check_key_derivation: @$(MAKE) -C key_derivation @@ -138,7 +138,7 @@ endif #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub clean @$(MAKE) -C key_derivation clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET)_*.enc $(TARGET).elf From da4107996a2cd30db10f24296c62567066c9ad39 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 19:17:20 -0700 Subject: [PATCH 094/118] exo2: first pass at backwards-compat --- exosphere/program/source/boot/secmon_main.cpp | 4 + exosphere/program/source/secmon_setup.cpp | 41 ++++-- .../program/source/smc/secmon_smc_aes.cpp | 138 ++++++++++++++---- .../smc/secmon_smc_device_unique_data.cpp | 11 +- .../smc/secmon_smc_device_unique_data.hpp | 2 +- .../program/source/smc/secmon_smc_handler.cpp | 18 +++ .../program/source/smc/secmon_smc_handler.hpp | 2 + .../smc/secmon_smc_power_management.cpp | 13 +- .../program/source/smc/secmon_smc_rsa.cpp | 14 +- 9 files changed, 195 insertions(+), 48 deletions(-) diff --git a/exosphere/program/source/boot/secmon_main.cpp b/exosphere/program/source/boot/secmon_main.cpp index 6769cd9e7..db1e1e090 100644 --- a/exosphere/program/source/boot/secmon_main.cpp +++ b/exosphere/program/source/boot/secmon_main.cpp @@ -17,6 +17,7 @@ #include "secmon_boot.hpp" #include "secmon_boot_functions.hpp" #include "../smc/secmon_random_cache.hpp" +#include "../smc/secmon_smc_handler.hpp" #include "../secmon_cache.hpp" #include "../secmon_cpu_context.hpp" #include "../secmon_misc.hpp" @@ -180,6 +181,9 @@ namespace ams::secmon { /* Perform final initialization. */ secmon::SetupSocProtections(); secmon::SetupCpuSErrorDebug(); + + /* Configure the smc handler tables to reflect the current target firmware. */ + secmon::smc::ConfigureSmcHandlersForTargetFirmware(); } } diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp index 254b8e51f..f3daa86fc 100644 --- a/exosphere/program/source/secmon_setup.cpp +++ b/exosphere/program/source/secmon_setup.cpp @@ -584,6 +584,11 @@ namespace ams::secmon { MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + /* If we're cold-booting and on 1.0.0, alter the default carveout size. */ + if (g_is_cold_boot && GetTargetFirmware() <= TargetFirmware_1_0_0) { + g_kernel_carveouts[0].size = 200 * 128_KB; + } + /* Configure the two kernel carveouts. */ SetupKernelCarveouts(); @@ -599,10 +604,15 @@ namespace ams::secmon { reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_3, ~0u); reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_4, ~0u); - /* Configure ASIDs 1-3 as secure, and all others as non-secure. */ - reg::Write(MC + MC_SMMU_ASID_SECURITY, MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, SECURE), - MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, SECURE), - MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, SECURE)); + /* On modern firmware, configure ASIDs 1-3 as secure, and all others as non-secure. */ + if (GetTargetFirmware() >= TargetFirmware_4_0_0) { + reg::Write(MC + MC_SMMU_ASID_SECURITY, MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, SECURE)); + } else { + /* Legacy firmware accesses the MC directly, though, and so correspondingly we must allow ASIDs to be edited by non-secure world. */ + reg::Write(MC + MC_SMMU_ASID_SECURITY, 0); + } reg::Write(MC + MC_SMMU_ASID_SECURITY_1, 0); reg::Write(MC + MC_SMMU_ASID_SECURITY_2, 0); @@ -1115,6 +1125,11 @@ namespace ams::secmon { /* Disable the ARC. */ DisableArc(); + /* Further protections are applied only on 4.0.0+. */ + if (GetTargetFirmware() < TargetFirmware_4_0_0) { + return; + } + /* Finalize and lock the carveout scratch registers. */ FinalizeCarveoutSecureScratchRegisters(); pmc::LockSecureRegister(pmc::SecureRegister_Carveout); @@ -1155,13 +1170,19 @@ namespace ams::secmon { } void SetupPmcAndMcSecure() { - /* Set the PMC secure. */ - reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE)); + const auto target_fw = GetTargetFirmware(); - /* Set the MC secure. */ - reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE), - SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE), - SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE)); + if (target_fw >= TargetFirmware_2_0_0) { + /* Set the PMC secure. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE)); + } + + if (target_fw >= TargetFirmware_4_0_0) { + /* Set the MC secure. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE)); + } } void SetupCpuCoreContext() { diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp index cfb992e33..7dabf7d43 100644 --- a/exosphere/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere/program/source/smc/secmon_smc_aes.cpp @@ -114,6 +114,9 @@ namespace ams::secmon::smc { struct DecryptDeviceUniqueDataOption { using DeviceUniqueDataIndex = util::BitPack32::Field<0, 3, DeviceUniqueData>; using Reserved = util::BitPack32::Field<3, 29, u32>; + + /* Legacy. */ + using EnforceDeviceUnique = util::BitPack32::Field<0, 1, bool>; }; constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = { @@ -499,7 +502,7 @@ namespace ams::secmon::smc { /* Decode arguments. */ std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source)); - const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast<int>(args.r[3]) - 1) : 0; + const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max<int>(pkg1::KeyGeneration_1_0_0, static_cast<int>(args.r[3]) - 1) : pkg1::KeyGeneration_1_0_0; /* Validate arguments. */ SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); @@ -517,11 +520,23 @@ namespace ams::secmon::smc { return SmcResult::Success; } - SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) { + constexpr size_t GetDiscountedMinimumDeviceUniqueDataSize(bool enforce_device_unique) { + if (enforce_device_unique) { + return 0; + } else { + return DeviceUniqueDataTotalMetaSize - DeviceUniqueDataIvSize; + } + } + + SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size, bool enforce_device_unique) { + /* Determine the discounted size towards the minimum. */ + const size_t discounted_size = GetDiscountedMinimumDeviceUniqueDataSize(enforce_device_unique); + SMC_R_UNLESS(enforce_device_unique || fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, InvalidArgument); + switch (mode) { case DeviceUniqueData_DecryptDeviceUniqueData: { - SMC_R_UNLESS(data_size < DeviceUniqueDataSizeMax, InvalidArgument); + SMC_R_UNLESS(DeviceUniqueDataTotalMetaSize - discounted_size < data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument); } break; case DeviceUniqueData_ImportLotusKey: @@ -529,7 +544,7 @@ namespace ams::secmon::smc { case DeviceUniqueData_ImportSslKey: case DeviceUniqueData_ImportEsClientCertKey: { - SMC_R_UNLESS(DeviceUniqueDataSizeMin <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument); + SMC_R_UNLESS(DeviceUniqueDataSizeMin - discounted_size <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument); } break; default: @@ -539,23 +554,9 @@ namespace ams::secmon::smc { return SmcResult::Success; } - SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { - /* Decode arguments. */ - u8 access_key[se::AesBlockSize]; - u8 key_source[se::AesBlockSize]; - - std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); - const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; - const uintptr_t data_address = args.r[4]; - const size_t data_size = args.r[5]; - std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); - - const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); - const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); - + SmcResult DecryptDeviceUniqueDataImpl(const u8 *access_key, const u8 *key_source, const DeviceUniqueData mode, const uintptr_t data_address, const size_t data_size, bool enforce_device_unique) { /* Validate arguments. */ - SMC_R_UNLESS(reserved == 0, InvalidArgument); - SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size, enforce_device_unique)); /* Decrypt the device unique data. */ alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax]; @@ -571,7 +572,7 @@ namespace ams::secmon::smc { const u8 * const seal_key_source = SealKeySources[seal_key_type]; /* Decrypt the data. */ - if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size)) { + if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size, enforce_device_unique)) { return SmcResult::InvalidArgument; } @@ -599,6 +600,89 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[se::AesBlockSize]; + u8 key_source[se::AesBlockSize]; + + std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); + + const auto mode = GetTargetFirmware() >= TargetFirmware_5_0_0 ? option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>() : DeviceUniqueData_DecryptDeviceUniqueData; + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + const bool enforce_device_unique = GetTargetFirmware() >= TargetFirmware_5_0_0 ? true : option.Get<DecryptDeviceUniqueDataOption::EnforceDeviceUnique>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + + /* Decrypt the device unique data. */ + return DecryptDeviceUniqueDataImpl(access_key, key_source, mode, data_address, data_size, enforce_device_unique); + } + + SmcResult DecryptAndImportEsDeviceKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[se::AesBlockSize]; + u8 key_source[se::AesBlockSize]; + + std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); + + const auto mode = DeviceUniqueData_ImportEsDeviceKey; + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + const bool enforce_device_unique = option.Get<DecryptDeviceUniqueDataOption::EnforceDeviceUnique>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + + /* Ensure that the key is exactly the correct size. */ + if (enforce_device_unique) { + SMC_R_UNLESS(data_size == util::AlignUp(2 * se::RsaSize + sizeof(u32), se::AesBlockSize) + DeviceUniqueDataTotalMetaSize, InvalidArgument); + } else { + SMC_R_UNLESS(data_size == util::AlignUp(2 * se::RsaSize + sizeof(u32), se::AesBlockSize) + DeviceUniqueDataIvSize, InvalidArgument); + } + + /* Decrypt the device unique data. */ + return DecryptDeviceUniqueDataImpl(access_key, key_source, mode, data_address, data_size, enforce_device_unique); + } + + SmcResult DecryptAndImportLotusKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[se::AesBlockSize]; + u8 key_source[se::AesBlockSize]; + + std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key)); + const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; + const uintptr_t data_address = args.r[4]; + const size_t data_size = args.r[5]; + std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source)); + + const auto mode = DeviceUniqueData_ImportLotusKey; + const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + + const bool enforce_device_unique = option.Get<DecryptDeviceUniqueDataOption::EnforceDeviceUnique>(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + + /* Ensure that the key is exactly the correct size. */ + if (enforce_device_unique) { + SMC_R_UNLESS(data_size == se::RsaSize + DeviceUniqueDataTotalMetaSize, InvalidArgument); + } else { + SMC_R_UNLESS(data_size == se::RsaSize + DeviceUniqueDataIvSize, InvalidArgument); + } + + /* Decrypt the device unique data. */ + return DecryptDeviceUniqueDataImpl(access_key, key_source, mode, data_address, data_size, enforce_device_unique); + } + SmcResult ReencryptDeviceUniqueDataImpl(SmcArguments &args) { /* Decode arguments. */ u8 access_key_dec[se::AesBlockSize]; @@ -617,9 +701,11 @@ namespace ams::secmon::smc { const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>(); const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>(); + const bool enforce_device_unique = true; + /* Validate arguments. */ SMC_R_UNLESS(reserved == 0, InvalidArgument); - SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size, enforce_device_unique)); /* Decrypt the device unique data. */ alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax]; @@ -640,7 +726,7 @@ namespace ams::secmon::smc { /* Determine the seal key to use. */ const u8 * const seal_key_source = SealKeySources[SealKey_ReencryptDeviceUniqueData]; - if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size)) { + if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size, enforce_device_unique)) { return SmcResult::InvalidArgument; } } @@ -721,13 +807,11 @@ namespace ams::secmon::smc { /* Legacy APIs. */ SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, DecryptAndImportEsDeviceKeyImpl); } SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, DecryptAndImportLotusKeyImpl); } /* Es encryption utilities. */ diff --git a/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp index 6505daa52..b6f6b2d4d 100644 --- a/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp @@ -90,13 +90,13 @@ namespace ams::secmon::smc { } - bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, bool enforce_device_unique) { /* Determine how much decrypted data there will be. */ - const size_t enc_size = src_size - DeviceUniqueDataOuterMetaSize; + const size_t enc_size = src_size - (enforce_device_unique ? DeviceUniqueDataOuterMetaSize : DeviceUniqueDataIvSize); const size_t dec_size = enc_size - DeviceUniqueDataInnerMetaSize; /* Ensure that our sizes are allowed. */ - AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize); + AMS_ABORT_UNLESS(src_size > (enforce_device_unique ? DeviceUniqueDataTotalMetaSize : DeviceUniqueDataIvSize)); AMS_ABORT_UNLESS(dst_size >= enc_size); /* Determine the extents of the data. */ @@ -120,6 +120,11 @@ namespace ams::secmon::smc { /* Decrypt the data. */ ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); + /* If we're not enforcing device unique, there's no mac/device id. */ + if (!enforce_device_unique) { + return true; + } + /* Compute the gmac. */ ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize); diff --git a/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp index a8e790151..10e5d2b84 100644 --- a/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp @@ -28,7 +28,7 @@ namespace ams::secmon::smc { constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; - bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size); + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, bool enforce_device_unique); void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high); } diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index 644139b3c..a3cc7929c 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -122,6 +122,15 @@ namespace ams::secmon::smc { { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey } }; + /* Deprecated handlerss. */ + constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = { + 0xC300100C, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptAndImportEsDeviceKey + }; + + constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = { + 0xC300100E, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptAndImportLotusKey + }; + constinit HandlerInfo g_kern_handlers[] = { { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, @@ -266,6 +275,15 @@ namespace ams::secmon::smc { } + void ConfigureSmcHandlersForTargetFirmware() { + const auto target_fw = GetTargetFirmware(); + + if (target_fw < TargetFirmware_5_0_0) { + g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo; + g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo; + } + } + void HandleSmc(int type, SmcArguments &args) { /* Get the table. */ const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]); diff --git a/exosphere/program/source/smc/secmon_smc_handler.hpp b/exosphere/program/source/smc/secmon_smc_handler.hpp index f05120951..4f1fc682b 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.hpp +++ b/exosphere/program/source/smc/secmon_smc_handler.hpp @@ -21,4 +21,6 @@ namespace ams::secmon::smc { using SmcHandler = SmcResult (*)(SmcArguments &args); + void ConfigureSmcHandlersForTargetFirmware(); + } diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index 93f053887..97bb80399 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -157,8 +157,9 @@ namespace ams::secmon::smc { AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); /* Validate that the bpmp is appropriately halted. */ + const bool jtag = IsJtagEnabled() || GetTargetFirmware() < TargetFirmware_4_0_0; AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), - FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); + FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED))); /* TODO */ } @@ -300,6 +301,9 @@ namespace ams::secmon::smc { } void LoadAndStartSc7BpmpFirmware() { + /* Set BPMP reset. */ + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE)); + /* Set the PMC as insecure, so that the BPMP firmware can access it. */ reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE)); @@ -319,9 +323,6 @@ namespace ams::secmon::smc { /* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */ actmon::StopMonitoringBpmp(); - /* Set BPMP reset. */ - reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE)); - /* Load the bpmp firmware. */ void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer<void>(); std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size); @@ -417,7 +418,9 @@ namespace ams::secmon::smc { pmic::EnableSleep(); /* Ensure that the soc is in a state valid for us to suspend. */ - ValidateSocStateForSuspend(); + if (GetTargetFirmware() >= TargetFirmware_2_0_0) { + ValidateSocStateForSuspend(); + } /* Configure the pmc for sc7 entry. */ pmc::ConfigureForSc7Entry(); diff --git a/exosphere/program/source/smc/secmon_smc_rsa.cpp b/exosphere/program/source/smc/secmon_smc_rsa.cpp index d84ce9b14..01104a503 100644 --- a/exosphere/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere/program/source/smc/secmon_smc_rsa.cpp @@ -43,6 +43,16 @@ namespace ams::secmon::smc { }; constexpr size_t ModularExponentiateByStorageKeyTableSize = util::size(ModularExponentiateByStorageKeyTable); + consteval u32 GetModeForImportRsaKey(ImportRsaKey import_key) { + for (size_t i = 0; i < ModularExponentiateByStorageKeyTableSize; ++i) { + if (static_cast<ImportRsaKey>(ModularExponentiateByStorageKeyTable[i]) == import_key) { + return i; + } + } + + AMS_ASSUME(false); + } + class PrepareEsDeviceUniqueKeyAsyncArguments { private: int generation; @@ -200,7 +210,7 @@ namespace ams::secmon::smc { const uintptr_t mod_address = args.r[2]; const util::BitPack32 option = { static_cast<u32>(args.r[3]) }; - const auto mode = option.Get<ModularExponentiateByStorageKeyOption::Mode>(); + const auto mode = GetTargetFirmware() >= TargetFirmware_5_0_0 ? option.Get<ModularExponentiateByStorageKeyOption::Mode>() : GetModeForImportRsaKey(ImportRsaKey_Lotus); const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>(); /* Validate arguments. */ @@ -250,7 +260,7 @@ namespace ams::secmon::smc { std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest)); const util::BitPack32 option = { static_cast<u32>(args.r[7]) }; - const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : 0; + const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max<int>(pkg1::KeyGeneration_1_0_0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : pkg1::KeyGeneration_1_0_0; const auto type = option.Get<PrepareEsDeviceUniqueKeyOption::Type>(); const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>(); From 6780b096cada1d9910d92bc4ec63491b7c6df3f5 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 19:28:38 -0700 Subject: [PATCH 095/118] exo2: address volca review commentary --- exosphere/loader_stub/source/secmon_loader_uncompress.cpp | 2 +- exosphere/program/source/secmon_setup.cpp | 4 ++-- libraries/libexosphere/include/exosphere/se/se_hash.hpp | 2 +- libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp | 5 +++-- libraries/libexosphere/source/se/se_execute.cpp | 1 + 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/exosphere/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere/loader_stub/source/secmon_loader_uncompress.cpp index a58e3c5a1..cd494e980 100644 --- a/exosphere/loader_stub/source/secmon_loader_uncompress.cpp +++ b/exosphere/loader_stub/source/secmon_loader_uncompress.cpp @@ -36,7 +36,7 @@ namespace ams::secmon::loader { void Uncompress() { while (true) { /* Read a control byte. */ - u8 control = this->ReadByte(); + const u8 control = this->ReadByte(); /* Copy what it specifies we should copy. */ this->Copy(this->GetCopySize(control >> 4)); diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp index f3daa86fc..5c49db908 100644 --- a/exosphere/program/source/secmon_setup.cpp +++ b/exosphere/program/source/secmon_setup.cpp @@ -791,8 +791,8 @@ namespace ams::secmon { void DisableArc() { /* Configure IRAM top/bottom to point to memory ends (disabling redirection). */ - reg::Write(MC + MC_IRAM_BOM, (~0u) & MC_IRAM_BOM_WRITE_MASK); - reg::Write(MC + MC_IRAM_TOM, ( 0u) & MC_IRAM_TOM_WRITE_MASK); + reg::Write(MC + MC_IRAM_BOM, MC_REG_BITS_VALUE(IRAM_BOM_IRAM_BOM, (~0u))); + reg::Write(MC + MC_IRAM_TOM, MC_REG_BITS_VALUE(IRAM_TOM_IRAM_TOM, ( 0u))); /* Lock the IRAM aperture. */ reg::ReadWrite(MC + MC_IRAM_REG_CTRL, MC_REG_BITS_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, DISABLED)); diff --git a/libraries/libexosphere/include/exosphere/se/se_hash.hpp b/libraries/libexosphere/include/exosphere/se/se_hash.hpp index 934ced609..e8eed4ddf 100644 --- a/libraries/libexosphere/include/exosphere/se/se_hash.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_hash.hpp @@ -21,7 +21,7 @@ namespace ams::se { constexpr inline int Sha256HashSize = crypto::Sha256Generator::HashSize; union Sha256Hash { - u8 bytes[Sha256HashSize / sizeof( u8)]; + u8 bytes[Sha256HashSize / sizeof(u8) ]; u32 words[Sha256HashSize / sizeof(u32)]; }; diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp index 72947de8e..107bf548d 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp @@ -544,7 +544,8 @@ DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECWRB, (135 - (MC_CLIENT_ACCESS_N DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSRD2, (136 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSWR2, (137 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); -constexpr inline u32 MC_IRAM_BOM_WRITE_MASK = 0xFFFFF000u; -constexpr inline u32 MC_IRAM_TOM_WRITE_MASK = 0xFFFFF000u; +DEFINE_MC_REG(IRAM_BOM_IRAM_BOM, 12, BITSIZEOF(u32) - 12); +DEFINE_MC_REG(IRAM_TOM_IRAM_TOM, 12, BITSIZEOF(u32) - 12); DEFINE_MC_REG_BIT_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, 0, ENABLED, DISABLED); + diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp index 4f811ee8a..29e42dfbc 100644 --- a/libraries/libexosphere/source/se/se_execute.cpp +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -26,6 +26,7 @@ namespace ams::se { u32 size; }; static_assert(util::is_pod<LinkedListEntry>::value); + static_assert(sizeof(LinkedListEntry) == 0xC); uintptr_t GetPhysicalAddress(const void *ptr) { const uintptr_t virt_address = reinterpret_cast<uintptr_t>(ptr); From 47512cc8a220090855b810b05c2d63607f85635d Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Thu, 11 Jun 2020 22:30:20 -0700 Subject: [PATCH 096/118] fusee: always use the exo2 (modern tz) key layout --- fusee/fusee-secondary/src/key_derivation.c | 47 +++++----------------- 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/fusee/fusee-secondary/src/key_derivation.c b/fusee/fusee-secondary/src/key_derivation.c index b101c7f34..190e37539 100644 --- a/fusee/fusee-secondary/src/key_derivation.c +++ b/fusee/fusee-secondary/src/key_derivation.c @@ -161,8 +161,8 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui void *tsec_root_key = (void *)((uintptr_t)tsec_root_keys + 0x10 * (rev - MASTERKEY_REVISION_620)); if (memcmp(tsec_root_key, zeroes, 0x10) != 0) { /* We got a valid key from emulation. */ - set_aes_keyslot(0xC, tsec_root_key, 0x10); - se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10); + set_aes_keyslot(0xD, tsec_root_key, 0x10); + se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10); memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10); } } @@ -179,8 +179,8 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui fclose(extkey_file); for (unsigned int rev = MASTERKEY_REVISION_620; rev < MASTERKEY_REVISION_MAX; rev++) { if (memcmp(extkeys.tsec_root_keys[rev - MASTERKEY_REVISION_620], zeroes, 0x10) != 0) { - set_aes_keyslot(0xC, extkeys.tsec_root_keys[rev - MASTERKEY_REVISION_620], 0x10); - se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10); + set_aes_keyslot(0xD, extkeys.tsec_root_keys[rev - MASTERKEY_REVISION_620], 0x10); + se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10); memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10); } else { memcpy(g_dec_keyblobs[rev].master_kek, extkeys.master_keks[rev], 0x10); @@ -198,7 +198,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui clear_aes_keyslot(0xE); /* Get needed data. */ - set_aes_keyslot(0xC, g_dec_keyblobs[available_revision].master_kek, 0x10); + set_aes_keyslot(0xD, g_dec_keyblobs[available_revision].master_kek, 0x10); /* Also set the Package1 key for the revision that is stored on the eMMC boot0 partition. */ if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_2_0) { @@ -206,42 +206,17 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui } /* Derive keys for Exosphere, lock critical keyslots. */ - switch (target_firmware) { - case ATMOSPHERE_TARGET_FIRMWARE_1_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_2_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_3_0_0: - decrypt_data_into_keyslot(0xD, 0xF, devicekey_seed, 0x10); - decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10); - break; - case ATMOSPHERE_TARGET_FIRMWARE_4_0_0: - decrypt_data_into_keyslot(0xD, 0xF, devicekey_4x_seed, 0x10); - decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); - decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10); - decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10); - break; - case ATMOSPHERE_TARGET_FIRMWARE_5_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_6_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_6_2_0: - case ATMOSPHERE_TARGET_FIRMWARE_7_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_8_0_0: - case ATMOSPHERE_TARGET_FIRMWARE_8_1_0: - case ATMOSPHERE_TARGET_FIRMWARE_9_0_0: - decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10); - decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); - decrypt_data_into_keyslot(0xD, 0xC, masterkey_seed, 0x10); - decrypt_data_into_keyslot(0xC, 0xC, masterkey_4x_seed, 0x10); - break; - default: - return -1; - } + decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10); + decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10); + decrypt_data_into_keyslot(0xC, 0xD, masterkey_4x_seed, 0x10); + decrypt_data_into_keyslot(0xD, 0xD, masterkey_seed, 0x10); /* Setup master key revision, derive older master keys for use. */ return mkey_detect_revision(fuse_get_retail_type() != 0); } static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware, uint32_t generation) { - unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (devkey_get_keyslot(generation)) : (KEYSLOT_SWITCH_DEVICEKEY); - + unsigned int keyslot = devkey_get_keyslot(generation); if (fuse_get_bootrom_patch_version() < 0x7F) { /* On dev units, use a fixed "all-zeroes" seed. */ @@ -273,7 +248,7 @@ static void generate_personalized_aes_key_for_bis(void *dst, const void *wrapped 0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8 }; - unsigned int keyslot = (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? (devkey_get_keyslot(generation)) : (KEYSLOT_SWITCH_DEVICEKEY); + unsigned int keyslot = devkey_get_keyslot(generation); /* Derive kek. */ decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_kek, 0x10); From 597bdded69366783f11e7d161a112724f18b158e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 00:46:21 -0700 Subject: [PATCH 097/118] fusee/exo2: more fixes to boot lower firmwares --- exosphere/Makefile | 8 +++++--- exosphere/program/source/smc/secmon_smc_aes.cpp | 2 +- fusee/fusee-secondary/src/nxboot.c | 6 +----- fusee/fusee-secondary/src/nxboot.h | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/exosphere/Makefile b/exosphere/Makefile index 8017acdef..00aca3834 100644 --- a/exosphere/Makefile +++ b/exosphere/Makefile @@ -14,11 +14,13 @@ exosphere.bin: program.lz4 boot_code.lz4 @printf LENY >> exosphere.bin @echo "Built exosphere.bin..." -program.lz4: - $(MAKE) -C program +program.lz4: build_program @cp program/program.lz4 program.lz4 @cp program/boot_code.lz4 boot_code.lz4 +build_program: + $(MAKE) -C program + warmboot.bin: $(MAKE) -C warmboot @cp warmboot/warmboot.bin warmboot.bin @@ -40,4 +42,4 @@ warmboot-clean: boot_code-clean: @rm -f boot_code.lz4 -.PHONY: all clean $(CLEAN_TARGETS) +.PHONY: all clean build_program $(CLEAN_TARGETS) diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp index 7dabf7d43..60912e78a 100644 --- a/exosphere/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere/program/source/smc/secmon_smc_aes.cpp @@ -572,7 +572,7 @@ namespace ams::secmon::smc { const u8 * const seal_key_source = SealKeySources[seal_key_type]; /* Decrypt the data. */ - if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size, enforce_device_unique)) { + if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, se::AesBlockSize, key_source, se::AesBlockSize, work_buffer, data_size, enforce_device_unique)) { return SmcResult::InvalidArgument; } diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 45a0bca02..6a87a34f1 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -954,11 +954,7 @@ uint32_t nxboot_main(void) { print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Reading Exosphère...\n"); /* Select the right address for Exosphère. */ - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - exosphere_memaddr = (void *)0x4002D000; - } else { - exosphere_memaddr = (void *)0x40030000; - } + exosphere_memaddr = (void *)0x40030000; /* Copy Exosphère to a good location or read it directly to it. */ if (loader_ctx->exosphere_path[0] != '\0') { diff --git a/fusee/fusee-secondary/src/nxboot.h b/fusee/fusee-secondary/src/nxboot.h index 8c640e2f5..8d007b1f9 100644 --- a/fusee/fusee-secondary/src/nxboot.h +++ b/fusee/fusee-secondary/src/nxboot.h @@ -35,7 +35,7 @@ typedef struct { #define MAILBOX_NX_BOOTLOADER_BASE_100_620 0x40002E00 #define MAILBOX_NX_BOOTLOADER_BASE_700 0x40000000 -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) ? (MAILBOX_NX_BOOTLOADER_BASE_700) : (MAILBOX_NX_BOOTLOADER_BASE_100_620)) +#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (MAILBOX_NX_BOOTLOADER_BASE_700) #define MAKE_MAILBOX_NX_BOOTLOADER_REG(targetfw, n) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + n) #define MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(targetfw) (MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0x10) From 1047ceab983f2628adefc469a6a4ecb7e505d058 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 01:29:54 -0700 Subject: [PATCH 098/118] exo2/fusee: full 1.0.0 support. --- .../program/source/smc/secmon_smc_handler.cpp | 6 +++--- .../source/smc/secmon_smc_power_management.cpp | 4 +++- fusee/fusee-secondary/src/nxboot.c | 18 ++++++------------ fusee/fusee-secondary/src/nxboot.h | 12 +++++------- fusee/fusee-secondary/src/nxboot_iram.c | 18 ++++-------------- 5 files changed, 21 insertions(+), 37 deletions(-) diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index a3cc7929c..005a4f8a3 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -124,11 +124,11 @@ namespace ams::secmon::smc { /* Deprecated handlerss. */ constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = { - 0xC300100C, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptAndImportEsDeviceKey + 0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey }; constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = { - 0xC300100E, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptAndImportLotusKey + 0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey }; constinit HandlerInfo g_kern_handlers[] = { @@ -231,7 +231,7 @@ namespace ams::secmon::smc { SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) { /* Check if the smc is restricted. */ - if (AMS_UNLIKELY(IsHandlerRestricted(info))) { + if (GetTargetFirmware() >= TargetFirmware_4_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) { return SmcResult::NotPermitted; } diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index 97bb80399..66251b42e 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -348,7 +348,9 @@ namespace ams::secmon::smc { SaveSecureContext(); /* Load and start the sc7 firmware on the bpmp. */ - LoadAndStartSc7BpmpFirmware(); + if (GetTargetFirmware() >= TargetFirmware_2_0_0) { + LoadAndStartSc7BpmpFirmware(); + } /* Log our suspension. */ /* NOTE: Nintendo only does this on dev, but we will always do it. */ diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 6a87a34f1..c89127b04 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -598,8 +598,8 @@ static void nxboot_move_bootconfig() { fclose(bcfile); /* Select the actual BootConfig size and destination address. */ - bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) ? 0x4003D000 : 0x4003F800; - bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) ? 0x3000 : 0x1000; + bootconfig_addr = 0x4003F800; + bootconfig_size = 0x800; /* Copy the BootConfig into IRAM. */ memset((void *)bootconfig_addr, 0, bootconfig_size); @@ -870,7 +870,9 @@ uint32_t nxboot_main(void) { /* Initialize Boot Reason on older firmware versions. */ if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Initializing Boot Reason...\n"); - nxboot_set_bootreason((void *)MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(target_firmware)); + nxboot_set_bootreason((void *)MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE); + } else { + memset((void *)MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE, 0, 0x200); } /* Read the warmboot firmware from a file, otherwise from Atmosphere's implementation. */ @@ -919,15 +921,7 @@ uint32_t nxboot_main(void) { } /* Select the right address for the warmboot firmware. */ - if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - warmboot_memaddr = (void *)0x8000D000; - } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_0_0) { - warmboot_memaddr = (void *)0x4003B000; - } else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { - warmboot_memaddr = (void *)0x4003D800; - } else { - warmboot_memaddr = (void *)0x4003E000; - } + warmboot_memaddr = (void *)0x4003E000; print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Copying warmboot firmware...\n"); diff --git a/fusee/fusee-secondary/src/nxboot.h b/fusee/fusee-secondary/src/nxboot.h index 8d007b1f9..ebcc878b3 100644 --- a/fusee/fusee-secondary/src/nxboot.h +++ b/fusee/fusee-secondary/src/nxboot.h @@ -33,14 +33,12 @@ typedef struct { char nintendo_path[0x80]; } emummc_config_t; -#define MAILBOX_NX_BOOTLOADER_BASE_100_620 0x40002E00 -#define MAILBOX_NX_BOOTLOADER_BASE_700 0x40000000 -#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) (MAILBOX_NX_BOOTLOADER_BASE_700) -#define MAKE_MAILBOX_NX_BOOTLOADER_REG(targetfw, n) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE(targetfw) + n) +#define MAILBOX_NX_BOOTLOADER_BASE 0x40000000 +#define MAKE_MAILBOX_NX_BOOTLOADER_REG(n) MAKE_REG32(MAILBOX_NX_BOOTLOADER_BASE + n) -#define MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(targetfw) (MAILBOX_NX_BOOTLOADER_BASE(targetfw) + 0x10) -#define MAILBOX_NX_BOOTLOADER_SETUP_STATE(targetfw) MAKE_MAILBOX_NX_BOOTLOADER_REG(targetfw, 0xF8) -#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(targetfw) MAKE_MAILBOX_NX_BOOTLOADER_REG(targetfw, 0xFC) +#define MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE MAKE_MAILBOX_NX_BOOTLOADER_REG(0x10) +#define MAILBOX_NX_BOOTLOADER_SETUP_STATE MAKE_MAILBOX_NX_BOOTLOADER_REG(0xF8) +#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE MAKE_MAILBOX_NX_BOOTLOADER_REG(0xFC) #define NX_BOOTLOADER_STATE_INIT 0 #define NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG 1 diff --git a/fusee/fusee-secondary/src/nxboot_iram.c b/fusee/fusee-secondary/src/nxboot_iram.c index 17df294c5..3fc9ff3d0 100644 --- a/fusee/fusee-secondary/src/nxboot_iram.c +++ b/fusee/fusee-secondary/src/nxboot_iram.c @@ -28,15 +28,9 @@ #include "sysreg.h" void nxboot_finish(uint32_t boot_memaddr) { - uint32_t target_firmware = MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware; - /* Boot up Exosphère. */ - MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(target_firmware) = 0; - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_LOADED_PACKAGE2; - } else { - MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X; - } + MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0; + MAILBOX_NX_BOOTLOADER_SETUP_STATE = NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X; /* Terminate the display. */ display_end(); @@ -58,16 +52,12 @@ void nxboot_finish(uint32_t boot_memaddr) { } /* Wait for Exosphère to wake up. */ - while (MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE(target_firmware) == 0) { + while (MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE == 0) { udelay(1); } /* Signal Exosphère. */ - if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { - MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_FINISHED; - } else { - MAILBOX_NX_BOOTLOADER_SETUP_STATE(target_firmware) = NX_BOOTLOADER_STATE_FINISHED_4X; - } + MAILBOX_NX_BOOTLOADER_SETUP_STATE = NX_BOOTLOADER_STATE_FINISHED_4X; /* Halt ourselves in waitevent state. */ while (1) { From 37d13f92a813d51002ec9bd5ac59595f4b56d5e2 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 01:34:36 -0700 Subject: [PATCH 099/118] ams: bump to 0.13.0, add 10.0.4 recognition --- fusee/fusee-secondary/src/nxboot.c | 1 + libraries/libvapours/include/vapours/ams/ams_api_version.h | 4 ++-- .../libvapours/include/vapours/ams/ams_target_firmware.h | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index c89127b04..0fa866ed7 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -236,6 +236,7 @@ static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + CHECK_NCA("34728c771299443420820d8ae490ea41", 10_0_4); CHECK_NCA("5b1df84f88c3334335bbb45d8522cbb4", 10_0_3); CHECK_NCA("e951bc9dedcd54f65ffd83d4d050f9e0", 10_0_2); CHECK_NCA("36ab1acf0c10a2beb9f7d472685f9a89", 10_0_1); diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index 15e56be44..a7ec6545c 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -16,11 +16,11 @@ #pragma once #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 -#define ATMOSPHERE_RELEASE_VERSION_MINOR 12 +#define ATMOSPHERE_RELEASE_VERSION_MINOR 13 #define ATMOSPHERE_RELEASE_VERSION_MICRO 0 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 10 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 3 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 4 diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index 75ab4cedc..3273cf24c 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -52,8 +52,9 @@ #define ATMOSPHERE_TARGET_FIRMWARE_10_0_1 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 1) #define ATMOSPHERE_TARGET_FIRMWARE_10_0_2 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 2) #define ATMOSPHERE_TARGET_FIRMWARE_10_0_3 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 3) +#define ATMOSPHERE_TARGET_FIRMWARE_10_0_4 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 4) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_0_3 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_0_4 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT From 79e4c0ef6e0dbd3cfcf3890ed98ac62fa8e0c37e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 02:21:08 -0700 Subject: [PATCH 100/118] exo2: add security checks, full 2.0.0 support --- exosphere/program/source/secmon_setup.cpp | 14 ++++---- .../program/source/secmon_setup_warm.cpp | 20 +++++------ .../smc/secmon_smc_power_management.cpp | 36 +++++++++++++++++-- .../exosphere/tegra/tegra_ahb_arbc.hpp | 5 ++- .../include/exosphere/tegra/tegra_clkrst.hpp | 10 +++++- 5 files changed, 62 insertions(+), 23 deletions(-) diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp index 5c49db908..7c9ce28e8 100644 --- a/exosphere/program/source/secmon_setup.cpp +++ b/exosphere/program/source/secmon_setup.cpp @@ -1125,8 +1125,8 @@ namespace ams::secmon { /* Disable the ARC. */ DisableArc(); - /* Further protections are applied only on 4.0.0+. */ - if (GetTargetFirmware() < TargetFirmware_4_0_0) { + /* Further protections aren't applied on <= 1.0.0. */ + if (GetTargetFirmware() <= TargetFirmware_1_0_0) { return; } @@ -1156,16 +1156,16 @@ namespace ams::secmon { util::WaitMicroSeconds(1); } + /* Enable clock to the activity monitor. */ + clkrst::EnableActmonClock(); + /* If JTAG is disabled, disable JTAG. */ if (!secmon::IsJtagEnabled()) { reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, DISABLED)); - /* If version is above 4.0.0, turn on the activity monitor to prevent booting up the bpmp. */ - if (GetTargetFirmware() >= TargetFirmware_4_0_0) { - clkrst::EnableActmonClock(); - actmon::StartMonitoringBpmp(ActmonInterruptHandler); - } + /* Turn on the activity monitor to prevent booting up the bpmp. */ + actmon::StartMonitoringBpmp(ActmonInterruptHandler); } } diff --git a/exosphere/program/source/secmon_setup_warm.cpp b/exosphere/program/source/secmon_setup_warm.cpp index 560359339..331502e02 100644 --- a/exosphere/program/source/secmon_setup_warm.cpp +++ b/exosphere/program/source/secmon_setup_warm.cpp @@ -238,22 +238,18 @@ namespace ams::secmon { /* Set MSELECT config to set WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ /* and clear ERR_RESP_EN_SLAVE1(PCIe) | ERR_RESP_EN_SLAVE2(GPU) */ { - auto mselect_cfg = reg::Read(MSELECT(MSELECT_CONFIG)); - mselect_cfg &= ~(1u << 24); /* Clear ERR_RESP_EN_SLAVE1(PCIe) */ - mselect_cfg &= ~(1u << 25); /* Clear ERR_RESP_EN_SLAVE2(GPU) */ - mselect_cfg |= (1u << 27); /* Set WRAP_TO_INCR_SLAVE0(APC) */ - mselect_cfg |= (1u << 28); /* Set WRAP_TO_INCR_SLAVE1(PCIe) */ - mselect_cfg |= (1u << 29); /* Set WRAP_TO_INCR_SLAVE2(GPU) */ - reg::Write(MSELECT(MSELECT_CONFIG), mselect_cfg); + reg::ReadWrite(MSELECT(MSELECT_CONFIG), MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, ENABLE)); } /* Disable USB, USB2, AHB-DMA from arbitration. */ { - auto arb_dis = reg::Read(AHB_ARBC(AHB_ARBITRATION_DISABLE)); - arb_dis |= (1u << 5); /* Disable AHB-DMA */ - arb_dis |= (1u << 6); /* Disable USB */ - arb_dis |= (1u << 18); /* Disable USB2 */ - reg::Write(AHB_ARBC(AHB_ARBITRATION_DISABLE), arb_dis); + reg::ReadWrite(AHB_ARBC(AHB_ARBITRATION_DISABLE), AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_AHBDMA, DISABLE), + AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB, DISABLE), + AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB2, DISABLE)); } /* Select high priority group with priority 7. */ diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index 66251b42e..eb65e7243 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -44,6 +44,7 @@ namespace ams::secmon::smc { constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress(); constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t AHB_ARBC = MemoryRegionVirtualDeviceSystem.GetAddress(); constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1)); @@ -157,11 +158,42 @@ namespace ams::secmon::smc { AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); /* Validate that the bpmp is appropriately halted. */ - const bool jtag = IsJtagEnabled() || GetTargetFirmware() < TargetFirmware_4_0_0; + const bool jtag = IsJtagEnabled(); AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED))); - /* TODO */ + /* Validate that USB2, APB-DMA, AHB-DMA are held in reset. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_USB2_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_APBDMA_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_AHBDMA_RST, ENABLE))); + + /* Validate that USBD is held in reset. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_USBD_RST, ENABLE))); + + /* Validate that AHB-DMA, USB, USB2, COP are not allowed to arbitrate on the AHB. */ + AMS_ABORT_UNLESS(reg::HasValue(AHB_ARBC + AHB_ARBITRATION_DISABLE, AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_COP, DISABLE), + AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_AHBDMA, DISABLE), + AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB, DISABLE), + AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB2, DISABLE))); + + /* Validate that the GPIO controller has clock enabled. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_GPIO, ENABLE))); + + /* Validate that both FUSE and KFUSE have clock enabled. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_H_CLK_ENB_FUSE, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_H_CLK_ENB_KFUSE, ENABLE))); + + /* Validate that all of IRAM has clock enabled. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMA, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMB, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMC, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMD, ENABLE))); + + /* Validate that ACTMON has clock enabled. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_V_CLK_ENB_ACTMON, ENABLE))); + + /* Validate that ENTROPY has clock enabled. */ + AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_W_CLK_ENB_ENTROPY, ENABLE))); } void GenerateCryptographicallyRandomBytes(void * const dst, int size) { diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp index 74bf76ee6..d8a5d4321 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp @@ -35,4 +35,7 @@ #define DEFINE_AHB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) #define DEFINE_AHB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) -DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_COP, 1, ENABLE, DISABLE); +DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_COP, 1, ENABLE, DISABLE); +DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_AHBDMA, 5, ENABLE, DISABLE); +DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_USB, 6, ENABLE, DISABLE); +DEFINE_AHB_REG_BIT_ENUM(ARBITRATION_DISABLE_USB2, 18, ENABLE, DISABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 649ed3f04..ae22a0f70 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -198,10 +198,15 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA HANDLER(L, RTC, 0, 4) \ HANDLER(L, TMR, 0, 5) \ HANDLER(L, GPIO, 0, 8) \ + HANDLER(L, USBD, 0, 22) \ HANDLER(L, CACHE2, 0, 31) \ HANDLER(H, MEM, 1, 0) \ + HANDLER(H, AHBDMA, 1, 1) \ + HANDLER(H, APBDMA, 1, 2) \ + HANDLER(H, USB2, 1, 26) \ HANDLER(H, PMC, 1, 6) \ HANDLER(H, FUSE, 1, 7) \ + HANDLER(H, KFUSE, 1, 8) \ HANDLER(H, I2C5, 1, 15) \ HANDLER(H, EMC, 1, 25) \ HANDLER(U, CSITE, 2, 9) \ @@ -213,6 +218,7 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA HANDLER(V, CPUG, 3, 0) \ HANDLER(V, MSELECT, 3, 3) \ HANDLER(V, SPDIF_DOUBLER, 3, 22) \ + HANDLER(V, ACTMON, 3, 23) \ HANDLER(V, TZRAM, 3, 30) \ HANDLER(V, SE, 3, 31) \ HANDLER(W, PCIERX0, 4, 2) \ @@ -239,10 +245,12 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA #define CLK_RST_DEFINE_SET_CLR_REG(REGISTER, DEVICE, REGISTER_INDEX, DEVICE_INDEX) \ DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_SET_SET_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLR_CLR_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(CLK_OUT_ENB_##REGISTER##_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_SET_SET_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \ DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_CLR_CLR_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \ - DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_##REGISTER##_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); \ + DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEVICES_##REGISTER##_SWR_##DEVICE##_RST, DEVICE_INDEX, DISABLE, ENABLE); CLK_RST_FOREACH_DEVICE(CLK_RST_DEFINE_SET_CLR_REG) From d165ec71625c080771cc5b3c40f6223e111a3573 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 03:27:29 -0700 Subject: [PATCH 101/118] stratosphere: fix command availability to include minor versions --- .../include/stratosphere/ncm/ncm_i_content_storage.hpp | 4 ++-- stratosphere/spl/source/spl_deprecated_service.hpp | 2 +- stratosphere/spl/source/spl_es_service.hpp | 4 ++-- stratosphere/spl/source/spl_fs_service.hpp | 2 +- stratosphere/spl/source/spl_manu_service.hpp | 2 +- stratosphere/spl/source/spl_ssl_service.hpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp index f310278a8..abe070674 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_storage.hpp @@ -113,9 +113,9 @@ namespace ams::ncm { MAKE_SERVICE_COMMAND_META(RevertToPlaceHolder, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(SetPlaceHolderSize, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(ReadContentIdFile, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderIdDeprecated, hos::Version_2_0_0, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderIdDeprecated, hos::Version_2_0_0, hos::Version_2_3_0), MAKE_SERVICE_COMMAND_META(GetRightsIdFromPlaceHolderId, hos::Version_3_0_0), - MAKE_SERVICE_COMMAND_META(GetRightsIdFromContentIdDeprecated, hos::Version_2_0_0, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(GetRightsIdFromContentIdDeprecated, hos::Version_2_0_0, hos::Version_2_3_0), MAKE_SERVICE_COMMAND_META(GetRightsIdFromContentId, hos::Version_3_0_0), MAKE_SERVICE_COMMAND_META(WriteContentForDebug, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetFreeSpaceSize, hos::Version_2_0_0), diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp index 76c86138b..11d49a7d9 100644 --- a/stratosphere/spl/source/spl_deprecated_service.hpp +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -114,7 +114,7 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(LoadTitleKey), - MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKeyDeprecated, hos::Version_2_0_0, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKeyDeprecated, hos::Version_2_0_0, hos::Version_2_3_0), MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKey, hos::Version_3_0_0), MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 829cc4a86..abbe0b59b 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -50,9 +50,9 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_0_0), + MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_1_0), MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKey, hos::Version_5_0_0), - MAKE_SERVICE_COMMAND_META(ImportEsKeyDeprecated, hos::Version_4_0_0, hos::Version_4_0_0), + MAKE_SERVICE_COMMAND_META(ImportEsKeyDeprecated, hos::Version_4_0_0, hos::Version_4_1_0), MAKE_SERVICE_COMMAND_META(ImportEsKey, hos::Version_5_0_0), MAKE_SERVICE_COMMAND_META(UnwrapTitleKey), MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKey, hos::Version_2_0_0), diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index b9a48781f..eace4735f 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -48,7 +48,7 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(ImportLotusKeyDeprecated, hos::Version_4_0_0, hos::Version_4_0_0), + MAKE_SERVICE_COMMAND_META(ImportLotusKeyDeprecated, hos::Version_4_0_0, hos::Version_4_1_0), MAKE_SERVICE_COMMAND_META(ImportLotusKey, hos::Version_5_0_0), MAKE_SERVICE_COMMAND_META(DecryptLotusMessage), MAKE_SERVICE_COMMAND_META(GenerateSpecificAesKey), diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp index b9afbb6f4..9e6342369 100644 --- a/stratosphere/spl/source/spl_manu_service.hpp +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -44,7 +44,7 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_0_0), + MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_1_0), MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKey, hos::Version_5_0_0), MAKE_SERVICE_COMMAND_META(ReEncryptRsaPrivateKey, hos::Version_5_0_0), }; diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index dfdeb6ac0..ba213b309 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -44,7 +44,7 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_0_0), + MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKeyDeprecated, hos::Version_4_0_0, hos::Version_4_1_0), MAKE_SERVICE_COMMAND_META(DecryptRsaPrivateKey, hos::Version_5_0_0), MAKE_SERVICE_COMMAND_META(ImportSslKey, hos::Version_5_0_0), MAKE_SERVICE_COMMAND_META(SslExpMod, hos::Version_5_0_0), From c827c0d5999ee02ad631f501a74c3dbd808d534c Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 03:34:06 -0700 Subject: [PATCH 102/118] exo2: properly perform smc restriction --- exosphere/program/source/smc/secmon_smc_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index 005a4f8a3..e79d7a007 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -231,7 +231,7 @@ namespace ams::secmon::smc { SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) { /* Check if the smc is restricted. */ - if (GetTargetFirmware() >= TargetFirmware_4_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) { + if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) { return SmcResult::NotPermitted; } From 73c1615cda753657da3ef9bf0bca8b5aa2ecf04b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 03:43:49 -0700 Subject: [PATCH 103/118] exo2: only enforce soc device preconditions when they are guaranteed to apply --- exosphere/program/source/smc/secmon_smc_power_management.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index eb65e7243..99008ddf6 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -162,6 +162,11 @@ namespace ams::secmon::smc { AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED))); + /* Further validations aren't guaranteed on < 6.0.0. */ + if (GetTargetFirmware() < TargetFirmware_6_0_0) { + return; + } + /* Validate that USB2, APB-DMA, AHB-DMA are held in reset. */ AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_USB2_RST, ENABLE), CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_APBDMA_RST, ENABLE), From 43f5a0ef4541c9d4820e0afc84e23d5830bc10a8 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 06:16:58 -0700 Subject: [PATCH 104/118] exo2: account for sleep/wake enabling jtag --- exosphere/Makefile | 6 ++++-- exosphere/program/source/secmon_setup.cpp | 4 +++- exosphere/warmboot/source/warmboot_main.cpp | 5 ++--- exosphere/warmboot/source/warmboot_misc.cpp | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/exosphere/Makefile b/exosphere/Makefile index 00aca3834..bf39b341b 100644 --- a/exosphere/Makefile +++ b/exosphere/Makefile @@ -21,10 +21,12 @@ program.lz4: build_program build_program: $(MAKE) -C program -warmboot.bin: - $(MAKE) -C warmboot +warmboot.bin: build_warmboot @cp warmboot/warmboot.bin warmboot.bin +build_warmboot: + $(MAKE) -C warmboot + boot_code.lz4: program.lz4 exosphere-clean: diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp index 7c9ce28e8..a87539e72 100644 --- a/exosphere/program/source/secmon_setup.cpp +++ b/exosphere/program/source/secmon_setup.cpp @@ -949,7 +949,9 @@ namespace ams::secmon { void SetupForLp0Exit() { /* Exit HiZ mode in charger, if we need to. */ - if (smc::IsChargerHiZModeEnabled()) { + const auto target_fw = GetTargetFirmware(); + const bool force_exit_hiz_mode = (target_fw < TargetFirmware_4_0_0) || (target_fw < TargetFirmware_8_0_0 && fuse::GetHardwareType() == fuse::HardwareType_Icosa); + if (force_exit_hiz_mode || smc::IsChargerHiZModeEnabled()) { ExitChargerHiZMode(); } diff --git a/exosphere/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp index 933b17e37..5042fa8ad 100644 --- a/exosphere/warmboot/source/warmboot_main.cpp +++ b/exosphere/warmboot/source/warmboot_main.cpp @@ -81,10 +81,9 @@ namespace ams::warmboot { PowerOnCpu(); /* Halt ourselves. */ - const bool disable_jtag = metadata->target_firmware >= TargetFirmware_4_0_0; while (true) { - reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), - FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, disable_jtag, DISABLED, ENABLED)); + reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED)); } } diff --git a/exosphere/warmboot/source/warmboot_misc.cpp b/exosphere/warmboot/source/warmboot_misc.cpp index eeb5932f2..3c648a7e9 100644 --- a/exosphere/warmboot/source/warmboot_misc.cpp +++ b/exosphere/warmboot/source/warmboot_misc.cpp @@ -39,8 +39,8 @@ namespace ams::warmboot { const bool jtag_sts = reg::HasValue(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM(STICKY_BITS_JTAG_STS, ENABLE)); reg::ReadWrite(SYSTEM + SB_PFCFG, SB_REG_BITS_ENUM_SEL(PFCFG_DBGEN, jtag_sts, ENABLE, DISABLE), - SB_REG_BITS_ENUM_SEL(PFCFG_NIDEN, jtag_sts, ENABLE, DISABLE), SB_REG_BITS_ENUM_SEL(PFCFG_SPNIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM (PFCFG_NIDEN, ENABLE), SB_REG_BITS_ENUM (PFCFG_SPIDEN, DISABLE)); reg::ReadWrite(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM_SEL(PP_CONFIG_CTL_JTAG, jtag_sts, ENABLE, DISABLE)); From c129256dd0f4294dd60c15605884ce9fc48a6058 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 11:01:47 -0700 Subject: [PATCH 105/118] exo: amend pk21 restrictions --- .../program/source/boot/secmon_boot_functions.cpp | 10 ++++------ .../include/exosphere/pkg1/pkg1_boot_config.hpp | 8 ++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/exosphere/program/source/boot/secmon_boot_functions.cpp b/exosphere/program/source/boot/secmon_boot_functions.cpp index 08e42c8cc..b8782605a 100644 --- a/exosphere/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere/program/source/boot/secmon_boot_functions.cpp @@ -135,14 +135,12 @@ namespace ams::secmon::boot { void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header) { /* Check for all-zeroes signature. */ - bool is_decrypted = header.signature[0] == 0; - is_decrypted &= crypto::IsSameBytes(header.signature, header.signature + 1, sizeof(header.signature) - 1); + const bool is_unsigned = header.signature[0] == 0 && crypto::IsSameBytes(header.signature, header.signature + 1, sizeof(header.signature) - 1); + secmon::impl::GetBootConfigStorage()->signed_data.SetPackage2SignatureVerificationDisabled(is_unsigned); /* Check for valid magic. */ - is_decrypted &= crypto::IsSameBytes(header.meta.magic, pkg2::Package2Meta::Magic::String, sizeof(header.meta.magic)); - - /* Set the setting in boot config. */ - secmon::impl::GetBootConfigStorage()->signed_data.SetPackage2Decrypted(is_decrypted); + const bool is_decrypted = crypto::IsSameBytes(header.meta.magic, pkg2::Package2Meta::Magic::String, sizeof(header.meta.magic)); + secmon::impl::GetBootConfigStorage()->signed_data.SetPackage2EncryptionDisabled(is_decrypted); } void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify) { diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp index 5d37aa2cd..8f67ab2f4 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -123,8 +123,12 @@ namespace ams::pkg1 { return (this->flags1[0] & (1 << 0)) != 0; } - constexpr void SetPackage2Decrypted(bool decrypted) { - this->flags |= decrypted ? 0x3 : 0x0; + constexpr void SetPackage2SignatureVerificationDisabled(bool decrypted) { + this->flags |= decrypted ? (1 << 1) : (0 << 0); + } + + constexpr void SetPackage2EncryptionDisabled(bool decrypted) { + this->flags |= decrypted ? (1 << 0) : (0 << 0); } }; static_assert(util::is_pod<BootConfigSignedData>::value); From b966345b255c0fd76031e4e4766c9ea8dcdc1354 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Fri, 12 Jun 2020 12:09:49 -0700 Subject: [PATCH 106/118] exo2: correct pkg2 encryption key load --- .../program/source/boot/secmon_package2.cpp | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/exosphere/program/source/boot/secmon_package2.cpp b/exosphere/program/source/boot/secmon_package2.cpp index 32e8e8a7c..648d615fd 100644 --- a/exosphere/program/source/boot/secmon_package2.cpp +++ b/exosphere/program/source/boot/secmon_package2.cpp @@ -37,6 +37,25 @@ namespace ams::secmon::boot { return VerifySignature(header.signature, sizeof(header.signature), mod, mod_size, std::addressof(header.meta), sizeof(header.meta)); } + int PrepareMasterKey(int key_generation) { + if (key_generation == GetKeyGeneration()) { + return pkg1::AesKeySlot_Master; + } + + constexpr int Slot = pkg1::AesKeySlot_Temporary; + LoadMasterKey(Slot, key_generation); + + return Slot; + } + + void PreparePackage2Key(int pkg2_slot, int key_generation, const void *key, size_t key_size) { + /* Get keyslot for the desired master key. */ + const int master_slot = PrepareMasterKey(key_generation); + + /* Load the package2 key into the desired keyslot. */ + se::SetEncryptedAesKey128(pkg2_slot, master_slot, key, key_size); + } + void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation) { /* Ensure that the SE sees consistent data. */ hw::FlushDataCache(key, key_size); @@ -44,14 +63,11 @@ namespace ams::secmon::boot { hw::FlushDataCache(dst, dst_size); hw::DataSynchronizationBarrierInnerShareable(); - /* Load the needed master key into the temporary keyslot. */ - secmon::LoadMasterKey(pkg1::AesKeySlot_Temporary, key_generation); - /* Load the package2 key into the temporary keyslot. */ - se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, pkg1::AesKeySlot_Temporary, key, key_size); + PreparePackage2Key(pkg1::AesKeySlot_Temporary, key_generation, key, key_size); /* Decrypt the data. */ - se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Temporary, src, src_size, iv, iv_size); + se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Temporary, src, src_size, iv, iv_size); /* Clear the keyslot we just used. */ se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); From d9c90835743b93ac18d7ae53fa8578ee0b3e178e Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 13 Jun 2020 00:08:32 -0700 Subject: [PATCH 107/118] emummc: cleanup pr per review --- emummc/source/main.c | 17 +++++++-------- emummc/source/utils/types.h | 41 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/emummc/source/main.c b/emummc/source/main.c index e93d3761d..8ec63f8d4 100644 --- a/emummc/source/main.c +++ b/emummc/source/main.c @@ -33,22 +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; -Handle self_proc_handle = 0; -u8 *fs_rw_mapping = NULL; + 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. @@ -63,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)) @@ -230,7 +231,7 @@ static void _map_fs_rw(void) { do { fs_rw_mapping = (u8 *)(smcGenerateRandomU64() & 0xFFFFFF000ull); - rc = svcMapProcessMemory(fs_rw_mapping, self_proc_handle, INJECT_OFFSET(u64, 0), fs_code_size); + rc = svcMapProcessMemory(fs_rw_mapping, self_proc_handle, FS_CODE_BASE, fs_code_size); } while (rc == 0xDC01 || rc == 0xD401); if (rc != 0) @@ -240,7 +241,7 @@ static void _map_fs_rw(void) { } static void _unmap_fs_rw(void) { - Result rc = svcUnmapProcessMemory(fs_rw_mapping, self_proc_handle, INJECT_OFFSET(u64, 0), fs_code_size); + Result rc = svcUnmapProcessMemory(fs_rw_mapping, self_proc_handle, FS_CODE_BASE, fs_code_size); if (rc != 0) { fatal_abort(Fatal_BadResult); @@ -250,7 +251,7 @@ static void _unmap_fs_rw(void) { } static void _write32(uintptr_t source, u32 value) { - *((u32 *)(fs_rw_mapping + (source - INJECT_OFFSET(u64, 0)))) = value; + *((u32 *)(fs_rw_mapping + (source - FS_CODE_BASE))) = value; } void hook_function(uintptr_t source, uintptr_t target) @@ -412,7 +413,7 @@ void __init() text_base = meminfo.addr; // Get code size - svcQueryMemory(&meminfo, &pageinfo, INJECT_OFFSET(u64, 0)); + svcQueryMemory(&meminfo, &pageinfo, FS_CODE_BASE); fs_code_size = meminfo.size; load_emummc_ctx(); diff --git a/emummc/source/utils/types.h b/emummc/source/utils/types.h index 5af01d48e..ce4db820c 100644 --- a/emummc/source/utils/types.h +++ b/emummc/source/utils/types.h @@ -31,26 +31,26 @@ #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. @@ -58,7 +58,6 @@ 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 From 23d3f786e3f9b285a6222a13d9292790a6b38bcb Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sat, 13 Jun 2020 00:10:06 -0700 Subject: [PATCH 108/118] git subrepo push emummc subrepo: subdir: "emummc" merged: "06ab9b89" upstream: origin: "https://github.com/m4xw/emuMMC" branch: "exo2" commit: "06ab9b89" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- emummc/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emummc/.gitrepo b/emummc/.gitrepo index df0193066..643c3ea7a 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emuMMC branch = exo2 - commit = 3791be9fd207811cce221a4311da63fda1d32d4a - parent = ff7bed5db7d441eecc9444a9068ae59e7f9b7744 + commit = 06ab9b895c4264ecc14d3bf9be1260e2096f6037 + parent = dccd41f6d25498c191a157123a27724203d3bc37 method = rebase cmdver = 0.4.1 From 0698338312f11c0f9616839c8698dc5982dc63f4 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:06:44 -0700 Subject: [PATCH 109/118] exo2: resolve remaining erista TODOs, clean up debugging code --- .../program/source/boot/secmon_boot_setup.cpp | 7 +- exosphere/program/source/secmon_error.cpp | 58 +++++++++++----- .../program/source/smc/secmon_smc_error.cpp | 17 ++++- .../program/source/smc/secmon_smc_handler.cpp | 68 ------------------- .../smc/secmon_smc_power_management.cpp | 2 +- 5 files changed, 62 insertions(+), 90 deletions(-) diff --git a/exosphere/program/source/boot/secmon_boot_setup.cpp b/exosphere/program/source/boot/secmon_boot_setup.cpp index 1f56214b6..2f7217f25 100644 --- a/exosphere/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere/program/source/boot/secmon_boot_setup.cpp @@ -60,7 +60,12 @@ namespace ams::secmon::boot { reg::ReadWrite(pmc + APBDEV_PMC_SECURE_SCRATCH21, REG_BITS_VALUE(4, 1, 1)); /* Write the warmboot key. */ - /* TODO */ + /* TODO: This is necessary for mariko. We should decide how to handle this. */ + /* In particular, mariko will need to support loading older-than-expected warmboot firmware. */ + /* We could hash the warmboot firmware and use a lookup table, or require bootloader to provide */ + /* The warmboot key as a parameter. The latter is a better solution, but it would be nice to take */ + /* care of it here. Perhaps we should read the number of anti-downgrade fuses burnt, and translate that */ + /* to the warmboot key? To be decided during the process of implementing ams-on-mariko support. */ } /* This function derives the master kek and device keys using the tsec root key. */ diff --git a/exosphere/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp index c05bf4cd7..5b576294b 100644 --- a/exosphere/program/source/secmon_error.cpp +++ b/exosphere/program/source/secmon_error.cpp @@ -16,12 +16,17 @@ #include <exosphere.hpp> #include "secmon_error.hpp" +namespace { + + constexpr bool SaveSystemStateForDebug = false; + +} + namespace ams::diag { - void AbortImpl() { - /* TODO: This is here for debugging. Remove this when exo2 is working. */ -#if 1 - { + namespace { + + ALWAYS_INLINE void SaveSystemStateForDebugAbort() { *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0xDDDDDDDD; u64 temp_reg; @@ -39,7 +44,14 @@ namespace ams::diag { util::WaitMicroSeconds(1000); } -#endif + + } + + void AbortImpl() { + /* Perform any necessary (typically none) debugging. */ + if constexpr (SaveSystemStateForDebug) { + SaveSystemStateForDebugAbort(); + } secmon::SetError(pkg1::ErrorInfo_UnknownAbort); secmon::ErrorReboot(); @@ -49,18 +61,11 @@ namespace ams::diag { namespace ams::secmon { - void SetError(pkg1::ErrorInfo info) { - const uintptr_t address = secmon::MemoryRegionVirtualDevicePmc.GetAddress() + PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH; + namespace { - if (reg::Read(address) == pkg1::ErrorInfo_None) { - reg::Write(address, info); - } - } + constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); - NORETURN void ErrorReboot() { - /* TODO: This is here for debugging. Remove this when exo2 is working. */ -#if 1 - { + ALWAYS_INLINE void SaveSystemStateForDebugErrorReboot() { u64 temp_reg; *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0x5A5A5A5A; @@ -92,14 +97,31 @@ namespace ams::secmon { util::WaitMicroSeconds(1000); } -#endif + + } + + void SetError(pkg1::ErrorInfo info) { + const uintptr_t address = secmon::MemoryRegionVirtualDevicePmc.GetAddress() + PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH; + + if (reg::Read(address) == pkg1::ErrorInfo_None) { + reg::Write(address, info); + } + } + + NORETURN void ErrorReboot() { + /* Perform any necessary (typically none) debugging. */ + if constexpr (SaveSystemStateForDebug) { + SaveSystemStateForDebugErrorReboot(); + } /* Lockout the security engine. */ se::Lockout(); - /* TODO: Lockout fuses. */ + /* Lockout fuses. */ + fuse::Lockout(); - /* TODO: Disable SE Crypto Operations. */ + /* Disable crypto operations after reboot. */ + reg::Write(PMC + APBDEV_PMC_CRYPTO_OP, 0); while (true) { wdt::Reboot(); diff --git a/exosphere/program/source/smc/secmon_smc_error.cpp b/exosphere/program/source/smc/secmon_smc_error.cpp index 70ea8f1db..28f7b277b 100644 --- a/exosphere/program/source/smc/secmon_smc_error.cpp +++ b/exosphere/program/source/smc/secmon_smc_error.cpp @@ -20,8 +20,21 @@ namespace ams::secmon::smc { SmcResult SmcShowError(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Decode arguments. */ + const u32 r = ((args.r[1] >> 8) & 0xF); + const u32 g = ((args.r[1] >> 4) & 0xF); + const u32 b = ((args.r[1] >> 0) & 0xF); + + const u32 rgb = (b << 8) | (g << 4) | (r << 0); + + /* Set the error info. */ + SetError(pkg1::MakeKernelPanicResetInfo(rgb)); + + /* Reboot. */ + ErrorReboot(); + + /* This point will never be reached. */ + __builtin_unreachable(); } } diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index e79d7a007..3c790e3a7 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -239,40 +239,6 @@ namespace ams::secmon::smc { return info.handler(args); } - constinit std::atomic<int> g_logged = 0; - - constexpr int LogMin = 0x1000000; - constexpr int LogMax = 0x1000000; - - constexpr size_t LogBufSize = 0x5000; - - void DebugLog(SmcArguments &args) { - const int current = g_logged.fetch_add(1); - - if (current == 0) { - std::memset(MemoryRegionVirtualDebug.GetPointer<void>(), 0xCC, LogBufSize); - } - - if (current < LogMin) { - return; - } - - const int ind = current - LogMin; - const int ofs = (ind * sizeof(args)) % LogBufSize; - - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + ofs))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; - } - - if (current >= LogMax) { - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } - - } - } void ConfigureSmcHandlersForTargetFirmware() { @@ -288,45 +254,11 @@ namespace ams::secmon::smc { /* Get the table. */ const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]); - if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { - DebugLog(args); - } - /* Get the handler info. */ const auto &info = GetHandlerInfo(table, args.r[0]); /* Set the invocation result. */ args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args)); - - if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { - DebugLog(args); - } - -/* TODO: For debugging. Remove this when exo2 is complete. */ -#if 1 - if (args.r[0] == static_cast<u64>(SmcResult::NotImplemented)) { - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB; - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id); - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; - } - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } - if (args.r[0] != static_cast<u64>(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) { - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC; - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id); - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i]; - } - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } -#endif } } diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index 99008ddf6..4af64ee37 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -325,7 +325,7 @@ namespace ams::secmon::smc { } void SaveSecureContextForMariko() { - /* TODO */ + /* TODO: Implement this when adding ams-on-mariko support. */ } void SaveSecureContext() { From f68d33b70aed8954cc2c539e5934bcaf37ba51da Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:18:13 -0700 Subject: [PATCH 110/118] ams: update roadmap documentation --- docs/roadmap.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 210e3b624..94b6cba6c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,12 +1,17 @@ # Planned Features 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 in late April of 2020. +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**: Under active development by SciresM -* **Estimated Time**: May 2020 +* **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. @@ -18,11 +23,6 @@ The following descriptions were last updated in late April of 2020. * **Development Status**: Under semi-active development by SciresM; temporarily on pause while the System Updater API is completed. * **Estimated Time**: Mid-to-Late 2020 -## exosphere re-write -* **Description**: exosphère, atmosphère's reimplementation of Horizon's Secure Monitor, 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. In addition, exosphère was written to conform to constraints that no longer apply in an environment where it is not launched from the web browser, and where using a custom firmware image to orchestrate wake-from-sleep is possible. exosphère currently uses all but 1 KB of the space available to it, putting it at risk of breaking as future firmware updates are supported. A re-write will solve these issues. -* **Development Status**: Planned. -* **Estimated Time**: 2020-2021. - ## 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. From 45a8c5a54a0c14a569de20b4403b53ffbb500e90 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:42:36 -0700 Subject: [PATCH 111/118] docs: update changelog for 0.13.0 --- docs/changelog.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 9f43dfdad..79f5bde52 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,28 @@ # 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. ++ 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. From 9b8ffdf0930c0283f3ccbcf386593ebd4007449a Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:44:08 -0700 Subject: [PATCH 112/118] docs/changelog: heh, right --- docs/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 79f5bde52..da0287668 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,6 +21,9 @@ + 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. From d236b88571ae8ec723038eb42b10605e5def69fe Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:50:08 -0700 Subject: [PATCH 113/118] hos: be a little friendlier about versioning --- .../source/hos/hos_version_api.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index 2d31bbbe6..02ec4d6f3 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -22,7 +22,7 @@ namespace ams::hos { hos::Version g_hos_version; bool g_has_cached; - os::Mutex g_mutex(false); + os::SdkMutex g_mutex; void CacheValues() { if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) { @@ -37,7 +37,21 @@ namespace ams::hos { /* Hos version is a direct copy of target firmware, just renamed. */ g_hos_version = static_cast<hos::Version>(exosphere::GetApiInfo().GetTargetFirmware()); - AMS_ABORT_UNLESS(g_hos_version <= hos::Version_Max); + + /* Ensure that this is a hos version we can sanely *try* to run. */ + /* To be friendly, we will only require that we recognize the major and minor versions. */ + /* We can consider only recognizing major in the future, but micro seems safe to ignore as + /* there are no breaking IPC changes in minor updates. */ + { + constexpr u32 MaxMajor = (static_cast<u32>(g_hos_version) >> 24) & 0xFF; + constexpr u32 MaxMinor = (static_cast<u32>(g_hos_version) >> 16) & 0xFF; + + const u32 major = (static_cast<u32>(g_hos_version) >> 24) & 0xFF; + const u32 minor = (static_cast<u32>(g_hos_version) >> 16) & 0xFF; + + const bool is_safely_tryable_version = (g_hos_version <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor); + AMS_ABORT_UNLESS(is_safely_tryable_version); + } __atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST); } From a680b35e09d9c87b11121c32990e7cbfcfb4ab88 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:53:39 -0700 Subject: [PATCH 114/118] hos: fix c/p error' --- libraries/libstratosphere/source/hos/hos_version_api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index 02ec4d6f3..af867f7e5 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -43,8 +43,8 @@ namespace ams::hos { /* We can consider only recognizing major in the future, but micro seems safe to ignore as /* there are no breaking IPC changes in minor updates. */ { - constexpr u32 MaxMajor = (static_cast<u32>(g_hos_version) >> 24) & 0xFF; - constexpr u32 MaxMinor = (static_cast<u32>(g_hos_version) >> 16) & 0xFF; + constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF; + constexpr u32 MaxMinor = (static_cast<u32>(hos::Version_Max) >> 16) & 0xFF; const u32 major = (static_cast<u32>(g_hos_version) >> 24) & 0xFF; const u32 minor = (static_cast<u32>(g_hos_version) >> 16) & 0xFF; From 2ed7f84523fc891e2275bf21ece831bee79ab853 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:55:18 -0700 Subject: [PATCH 115/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "adf5cd34" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "adf5cd34" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index 237468473..4e0c45571 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = 797dfa782e85173652d017de38066f9a5c88622a - parent = 79ae47f028ef9994fcd9c390d7523ceb37ad608c + commit = adf5cd345da71ad71e44e374d547c50aa01c84a7 + parent = a680b35e09d9c87b11121c32990e7cbfcfb4ab88 method = merge cmdver = 0.4.1 From 033ae1dbe09ba354849caf90ca2a2f114d9b3b4b Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:59:12 -0700 Subject: [PATCH 116/118] ams: fix comment warn --- libraries/libstratosphere/source/hos/hos_version_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index af867f7e5..d77e43fae 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -40,7 +40,7 @@ namespace ams::hos { /* Ensure that this is a hos version we can sanely *try* to run. */ /* To be friendly, we will only require that we recognize the major and minor versions. */ - /* We can consider only recognizing major in the future, but micro seems safe to ignore as + /* We can consider only recognizing major in the future, but micro seems safe to ignore as */ /* there are no breaking IPC changes in minor updates. */ { constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF; From 22ae311393ab3c1354a878e7167a996fd43d9050 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 22:59:53 -0700 Subject: [PATCH 117/118] git subrepo push libraries subrepo: subdir: "libraries" merged: "cf8f0c3c" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "cf8f0c3c" git-subrepo: version: "0.4.1" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index 4e0c45571..08a97c558 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = adf5cd345da71ad71e44e374d547c50aa01c84a7 - parent = a680b35e09d9c87b11121c32990e7cbfcfb4ab88 + commit = cf8f0c3c1f006e07c0b3976908220d3e7e83f7fa + parent = 033ae1dbe09ba354849caf90ca2a2f114d9b3b4b method = merge cmdver = 0.4.1 From 329513294613c547cd5164b72950c554eaf25917 Mon Sep 17 00:00:00 2001 From: Michael Scire <SciresM@gmail.com> Date: Sun, 14 Jun 2020 23:04:28 -0700 Subject: [PATCH 118/118] ams: update zip to include exosphere.ini template --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 7fbc96576..843bfd2e5 100644 --- a/Makefile +++ b/Makefile @@ -80,6 +80,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