Compare commits

...

211 Commits

Author SHA1 Message Date
Michael Scire
8e1cf7a7e0 erpt: amend logic for culling orphan attachments 2020-04-12 18:39:03 -07:00
Michael Scire
6b3bf10e46 fatal: update for latest bindings 2020-04-12 18:30:15 -07:00
Michael Scire
16a25309d2 erpt: reimplement the sysmodule 2020-04-11 21:40:14 -07:00
Michael Scire
eca5ac01b8 erpt: include all known types/categories in autogen 2020-04-10 03:33:30 -07:00
Michael Scire
50ea19e7a2 erpt: identify flags in autogen 2020-04-08 10:32:53 -07:00
Michael Scire
823a1f3ea3 erpt: tweak autogen 2020-04-08 10:09:56 -07:00
Michael Scire
b73895df0a util: add bitflagset 2020-04-08 08:39:36 -07:00
Michael Scire
5062329979 erpt: add ids (and autogeneration tool) 2020-04-08 07:17:42 -07:00
Michael Scire
065485b971 os: refactor/rewrite entire namespace. 2020-04-08 02:21:35 -07:00
Michael Scire
6193283f03 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "da6eac98"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "da6eac98"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-04-06 17:45:00 -07:00
Michael Scire
eb48e7cc59 buildsystem: fix building 2020-04-06 17:44:14 -07:00
Michael Scire
d98490d339 fs: Implement AesXtsStorage 2020-04-06 05:44:33 -07:00
Michael Scire
b2e86f5a1b fs: correct error result in AesCtrStorage 2020-04-06 04:56:49 -07:00
Michael Scire
0e9974e7b3 fs: add AesCtrStorage 2020-04-06 03:58:52 -07:00
Michael Scire
496be5ecd4 fs: implement PooledBuffer 2020-04-06 03:15:38 -07:00
Michael Scire
50a91b1d6e fs: implement system heap 2020-04-06 03:15:33 -07:00
Michael Scire
f872be67eb git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "18396d1a"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "18396d1a"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-04-05 23:26:06 -07:00
Michael Scire
e04679f05a crypto: add aes (ecb, ctr, xts) 2020-04-05 23:25:28 -07:00
Michael Scire
8d1ada2a1b fssystem: add RomFsFileSystem 2020-04-04 02:37:21 -07:00
Adubbz
a50d6a2696 NCM client implementation (#858)
* ncm: Implement InstallTaskDataBase and FileInstallTaskData

* ncm: minor bugfixes

* ncm: Implemented MemoryInstallTaskData

* ncm: more std

* ncm: begin implementing install task base

* ncm: move protected funcs

* ncm: fix recursive include

* ncm: more install task progress

* ncm install task: implement IncrementProgress and update UpdateThroughputMeasurement

* ncm: more work

* ncm client: more progress

* ncm client: more progress

* ncm client: finish implementing GetContentMetaInfoList

* ncm client: more progress

* ncm client: finished InstallTaskBase

* ncm client: implement PackageInstallTaskBase

* ncm client: fixes

* ncm: improve accuracy

* ncm client: implement PackageInstallTask

* ncm client: implement PackageSystemUpdateTask

* ncm client: minor name tweaks

* ncm client: implement SubmissionPackageInstallTask

* ncm client: add missing this to SubmissionPackageInstallTask

* ncm client: add missing nullptr check to SubmissionPackageInstallTask destructor

* ncm client: SubmissionPackageInstallTask fixes

* ncm: fix forward declarations

* ncm client: added simplified funcs

* ncm: cleanup client code

* ncm: fix bug introduced by cleanup

* ncm: fix typo

* ncm: implement correct ReadVariationContentMetaInfoList behavior

* ncm: correct InstallContentMetaWriter ctor

* ncm: correct conversion of content meta header types

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-04-03 22:40:46 -07:00
Michael Scire
76d72fa946 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "62f5667b"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "62f5667b"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-04-02 00:29:14 -07:00
Michael Scire
8b19fdfd51 sf: fix OutArray/InArray constructors to behave as expected 2020-04-02 00:28:39 -07:00
Michael Scire
612d846132 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "0f46474d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "0f46474d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-31 22:43:12 -07:00
Michael Scire
816ce605d3 fs: add an extension common name generator for sd card 2020-03-31 22:42:02 -07:00
SciresM
07c95662b1 nim: add DestroySystemUpdateTask/ListSystemUpdateTask (#863) 2020-03-31 12:50:55 -07:00
Michael Scire
2b930d21fd git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "de221b5d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "de221b5d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-30 21:41:49 -07:00
Michael Scire
0b52596087 fix CONCATENATE 2020-03-30 21:40:48 -07:00
Michael Scire
e9134d8044 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "63d5df84"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "63d5df84"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-30 20:41:45 -07:00
Michael Scire
33d6dfb6b3 updater: improve api 2020-03-30 20:39:56 -07:00
bunnei
6096fa0e45 KPageHeap: Fix a typo in initialization block alignment. (#862) 2020-03-30 19:27:02 -07:00
Michael Scire
058f265bd6 lmem: fix memory block header placement error 2020-03-30 00:56:57 -07:00
Michael Scire
bd4c608b08 ncm: use static memory pools for different allocations 2020-03-29 17:20:25 -07:00
Michael Scire
7fc1e86bf5 stratosphere: fix building with latest libnx 2020-03-29 15:24:40 -07:00
SciresM
87ec045a98 mem: implement most of StandardAllocator (#860)
This was tested using `https://github.com/node-dot-cpp/alloc-test` plus a few other by-hand tests.

It seems to work for the case we care about (sysmodules without thread cache-ing).

External users are advised to build with assertions on and contact SciresM if you find issues.

This is a lot of code to have gotten right in one go, and it was written mostly after midnight while sick, so there are probably un-noticed issues.
2020-03-29 14:43:16 -07:00
Michael Scire
7502e2174f git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "6f77a6bf"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "6f77a6bf"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-27 17:00:36 -07:00
Michael Scire
0545eb18c0 fs: add MountImageDirectory 2020-03-27 16:59:27 -07:00
Adubbz
0c161a4c1b fs: implement FileHandleStorage (#857)
* fs: implement FileHandleStorage

* fs: merge FileHandleStorage into file_storage TU

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-27 11:45:02 -07:00
Adubbz
3d518759da fssystem: Implement PartitionFileSystemCore (#856)
* fssystem: implement PartitionFileSystemMetaCore

* fssystem: PartitionFileSystemMetaCore cleanup

* fs: add IFile::DryWrite, update results

* fssystem: implement PartitionFileSystemCore

* fssystem: cleanup PartitionFileSystemCore

* fssystem: implement Sha256PartitionFileSystem

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-27 03:40:52 -07:00
Michael Scire
0af2758fde fs.mitm: use new namespace types for saves 2020-03-24 17:50:36 -07:00
Michael Scire
9bb5af9823 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "30f3e4c3"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "30f3e4c3"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-20 17:06:23 -07:00
Michael Scire
82eab9c8d0 Add Span<T>. 2020-03-20 17:04:01 -07:00
Michael Scire
3cca3801ca fs: fix bugs with external code filesystems 2020-03-20 11:47:19 -07:00
Michael Scire
03408f404a git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "07630f73"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "07630f73"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 20:06:10 -07:00
Michael Scire
92e7a3ca08 fs: add MountDeviceSaveData 2020-03-18 20:05:39 -07:00
Michael Scire
b27c7552d2 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "38fc51c6"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "38fc51c6"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 16:20:29 -07:00
Michael Scire
426257d4ae ams: bump version to 0.10.5 in prep for release later tonight 2020-03-18 16:19:59 -07:00
Michael Scire
7d34d599bb git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "07684b2c"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "07684b2c"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-18 00:15:32 -07:00
Michael Scire
067fe2d10f stratosphere: fix building with latest libnx 2020-03-18 00:14:34 -07:00
Michael Scire
4759c2f92c svc: add ipc accessor boilerplate 2020-03-18 00:09:51 -07:00
Michael Scire
ca26d8ce27 kern: Implement SvcManageNamedPort 2020-03-18 00:09:51 -07:00
Michael Scire
6c52cc3e26 memset: use neon-less impl, reformat other asm 2020-03-18 00:09:50 -07:00
Michael Scire
e42d3a3abf libmesosphere: use ARM-software/optimized-routines for memcpy/memset/memcmp 2020-03-18 00:09:50 -07:00
Michael Scire
884844bc23 svc: revert codegen changes 2020-03-18 00:09:50 -07:00
Michael Scire
f556db8c89 svc: make autogen asm register-clobber aware 2020-03-18 00:09:50 -07:00
Michael Scire
96d15b28c6 kern: implement CallSecureMonitor, some of GetInfo/GetSystemInfo 2020-03-18 00:09:50 -07:00
Michael Scire
37f7afb426 ams.mitm: greatly reduce memory requirements to build romfs 2020-03-18 00:07:19 -07:00
Michael Scire
7dd4e76c1d os: add rngmanager 2020-03-16 13:08:20 -07:00
Michael Scire
daa0deb1bf Add architecture-specific guard for get tick 2020-03-16 01:05:30 -07:00
Michael Scire
43bd733f0a os: implement Tick api, make build with -Werror 2020-03-16 01:02:55 -07:00
Michael Scire
70367e3e7c crypto: add Sha256Context 2020-03-11 03:26:55 -07:00
Michael Scire
45f8343659 kern: tweak KHandleTable impl 2020-03-10 04:54:53 -07:00
Michael Scire
e8dfe04701 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "a4ce1172"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "a4ce1172"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-03-09 18:04:11 -07:00
Michael Scire
95d5375158 ams: bump version to 0.10.4 2020-03-09 18:03:11 -07:00
hexkyz
b735bc53c4 stratosphere-ldr: fix code mount name 2020-03-09 21:53:40 +00:00
hexkyz
b4856a2d07 exo/fusee/sept: fix uart pinmuxing (thanks @CTCaer and @TuxSH) 2020-03-09 19:38:56 +00:00
hexkyz
ed4491a24f fusee/sept: fix volatile usage and simplify logic in gpio code (thanks @TuxSH) 2020-03-09 19:33:39 +00:00
Michael Scire
93004be59e ams: finish stdio -> fs bindings for stratosphere 2020-03-09 03:58:02 -07:00
Michael Scire
237b513408 loader: refactor to use fs bindings 2020-03-09 03:10:12 -07:00
Michael Scire
4c5e980e07 ams: dump tls in fatal error context 2020-03-08 17:01:53 -07:00
Michael Scire
08d9de6907 ldr: optimize memory usage for tracking system programs 2020-03-08 16:54:32 -07:00
Michael Scire
6eee3f5fe7 stratosphere-except-ldr: use fs bindings (this temporarily breaks loader) 2020-03-08 16:33:49 -07:00
Michael Scire
4eb3109c93 fusee: update fss0 header 2020-03-08 11:50:38 -07:00
Michael Scire
f7fb689412 fatal: use fs bindings 2020-03-08 04:50:47 -07:00
Michael Scire
2181adb82b creport: minor reformatting/fixes 2020-03-08 03:08:39 -07:00
Michael Scire
40c6733de3 creport: use fs bindings 2020-03-08 01:45:12 -08:00
Michael Scire
c703be86fc stratosphere template: allow for per-module defines 2020-03-08 01:39:16 -08:00
Michael Scire
f3732c72dc fusee: embed opt-in NCM kip 2020-03-08 00:21:01 -08:00
Adubbz
c7026b9094 Implement the NCM sysmodule (closes #91)
* Implement NCM

* Modernize ncm_main

* Remove unnecessary smExit

* Give access to svcCallSecureMonitor

* Stack size bump

* Fix incorrect setup for NandUser's content storage entry

* Fix a potential data abort when flushing the placeholder accessor cache

* Fix HasFile and HasDirectory

* Use r+b, not w+b

* Misc fixes

* errno begone

* Fixed more stdio error handling

* More main fixes

* Various command improvements

* Make dispatch tables great again

* Fix logic inversion

* Fixed content path generation

* Bump heap size, fix CleanupAllPlaceHolder

* Various fixes. Note: This contains debug stuff which will be removed later. I was getting tired of having to cherrypick tiny changes

* Fixed placeholder/content deletion

* Fixed incorrect content manager destruction

* Prevent automatic placeholder creation on open

* Fixed List implementation. Also lots of debug logging.

* Removed debug code

* Added a scope guard for WritePlaceHolder

* Manually prevent placeholder/content appending

* Revert "Removed debug code"

This reverts commit d6ff261fcc.

* Always cache placeholder file. Switch to ftell for preventing appending

* Universally use EnsureEnabled

* Abstract away file writing logic

* Misc cleanup

* Refactor placeholder cacheing

* Remove debug code (again)

* Revert "Remove debug code (again)"

This reverts commit 168447d80e.

* Misc changes

* Fixed file modes

* Fixed ContentId/PlaceHolderId alignment

* Improved type safety

* Fixed reinitialization

* Fixed doubleup on path creation

* Remove debug code

* Fixed 1.0.0 booting

* Correct amount of add on content

* Correct main thread stack size

* lr: Introducing registered data

* Reorder stratosphere Makefile

* Move results to libstrat

* lr: Cleanup lr_redirection

* lr: lr_manager tweaks

* lr: Imrpoved path handling and adjust ResolveAddOnContentPath order

* lr: Organise types

* Add eof newlines

* lr: Eliminate unnecessary vars

* lr: Unnecessary vars 2 electric boogaloo

* lr: Various helpers

* lr: RegisteredLocationResolver helpers

* ncm: Move ncm_types to libstrat

* ncm: Misc cleanup

* Implement NCM

* Modernize ncm_main

* Remove unnecessary smExit

* Give access to svcCallSecureMonitor

* Stack size bump

* Fix incorrect setup for NandUser's content storage entry

* Fix a potential data abort when flushing the placeholder accessor cache

* Fix HasFile and HasDirectory

* Use r+b, not w+b

* Misc fixes

* errno begone

* Fixed more stdio error handling

* More main fixes

* Various command improvements

* Make dispatch tables great again

* Fix logic inversion

* Fixed content path generation

* Bump heap size, fix CleanupAllPlaceHolder

* Various fixes. Note: This contains debug stuff which will be removed later. I was getting tired of having to cherrypick tiny changes

* Fixed placeholder/content deletion

* Fixed incorrect content manager destruction

* Prevent automatic placeholder creation on open

* Fixed List implementation. Also lots of debug logging.

* Removed debug code

* Added a scope guard for WritePlaceHolder

* Manually prevent placeholder/content appending

* Revert "Removed debug code"

This reverts commit d6ff261fcc.

* Always cache placeholder file. Switch to ftell for preventing appending

* Universally use EnsureEnabled

* Abstract away file writing logic

* Misc cleanup

* Refactor placeholder cacheing

* Remove debug code (again)

* Revert "Remove debug code (again)"

This reverts commit 168447d80e.

* Misc changes

* Fixed file modes

* Fixed ContentId/PlaceHolderId alignment

* Improved type safety

* Fixed reinitialization

* Fixed doubleup on path creation

* Remove debug code

* Fixed 1.0.0 booting

* Correct amount of add on content

* Correct main thread stack size

* lr: Introducing registered data

* Reorder stratosphere Makefile

* Move results to libstrat

* lr: Cleanup lr_redirection

* lr: lr_manager tweaks

* lr: Imrpoved path handling and adjust ResolveAddOnContentPath order

* lr: Organise types

* Add eof newlines

* lr: Eliminate unnecessary vars

* lr: Unnecessary vars 2 electric boogaloo

* lr: Various helpers

* lr: RegisteredLocationResolver helpers

* ncm: Move ncm_types to libstrat

* ncm: Misc cleanup

* Updated AddOnContentLocationResolver and RegisteredLocationResolver to 9.0.0

* Finished updating lr to 9.0.0

* Updated NCM to 9.0.0

* Fix libstrat includes

* Fixed application launching

* title_id_2 -> owner_tid

* Updated to new-ipc

* Change to using pure virtuals

* Title Id -> Program Id

* Fixed compilation against master

* std::scoped_lock<> -> std::scoped_lock

* Adopted R_UNLESS and R_CONVERT

* Prefix namespace to Results

* Adopt std::numeric_limits

* Fixed incorrect error handling in ReadFile

* Adopted AMS_ABORT_UNLESS

* Adopt util::GenerateUuid()

* Syntax improvements

* ncm_types: Address review

* Address more review comments

* Updated copyrights

* Address more feedback

* More feedback addressed

* More changes

* Move dispatch tables out of interface files

* Addressed remaining comments

* lr: move into libstratosphere

* ncm: Fix logic inversion

* lr: Add comments

* lr: Remove whitespace

* ncm: Start addressing feedback

* ncm: Cleanup InitializeContentManager

* lr: support client-side usage

* lr_service -> lr_api

* ncm: Begin refactoring content manager

* ncm: More content manager improvements

* ncm: Content manager mount improvements

* ldr: use lr bindings

* lr bindings usage: minor fixes

* ncm/lr: Pointer placement

* ncm: placeholder accessor cleanup

* ncm: minor fixes

* ncm: refactor rights cache

* ncm: content meta database cleanup

* ncm: move content meta database impl out of interface file

* ncm: Use const ContentMetaKey &

* ncm: fix other non-const ContentMetaKey references

* ncm: content meta database cleanup

* ncm: content storage fixes for 2.0.0

* ncm: add missing end of file newlines

* ncm: implement ContentMetaReader

* ncm: client-side api

* ncm: trim trailing spaces

* ncm: FS_MAX_PATH-1 -> fs::EntryNameLengthMax

* ncm: Use PathString and Path

* fs: implement accessor wrappers for ncm

* fs: implement user fs wrappers

* fs: add MountSdCard

* ncm: move to content manager impl

* ncm: fix up main

* kvdb: use fs::

* fs: Add wrappers needed for ncm

* ncm: use fs bindings, other refactoring

* ncm: minor fixes

* fsa: fix ReadFile without size output

* fs: add substorage, rom path tool

* ncm: fix dangling fsdev usage

* fs: fix bug in Commit

* fs: fixed incorrect mode check

* fs: implement Mount(System)Data

* ncm: don't delete hos

* results: add R_SUCCEED_IF

* ams-except-ncm: use R_SUCCEED_IF

* ncm: added comments

* ncm: fix api definitions

* ncm: use R_SUCCEED_IF

* pm: think of the savings

* ncm: employ kernel strats

* ncm: Nintendo has 5 MiB of heap. Give ourselves 4 to be safe, pending analysis

* ncm: refactor IDs, split types header into many headers

* ams.mitm: use fs bindings instead of stdio

* fs: SystemData uses SystemDataId

* ncm: improve meta-db accuracy

* ncm: inline getlatestkey

* fs: improve UnsupportedOperation results

* fs: modernize mount utils

* ams: misc fixes for merge-errors

* fs: improve unsupportedoperation results

* git subrepo pull emummc

subrepo:
  subdir:   "emummc"
  merged:   "d12dd546"
upstream:
  origin:   "https://github.com/m4xw/emuMMC"
  branch:   "develop"
  commit:   "d12dd546"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"

* util: add boundedmap

* ncm: minor style fixes

* ncm: don't unmount if mounting fails

* lr: bug fixes

* ncm: implement ncm.for-initialize + ncm.for-safemode

* lr: ncm::ProgramId::Invalid -> ncm::InvalidProgramId

* ncm: fix open directory mode on 1.0.0

* ncm: fix fs use, implement more of < 4.0.0 for-initialize/safemode

* ncm: implement packagedcontent -> content for building metadb

* ncm: fix save data flag management

* ncm: address some review suggestions (thanks @leoetlino!)

* updater: use fs bindings

* fs: implement MountCode

* fs: prefer make_unique to operator new

* ncm: implement remaining ContentMetaDatabaseBuilder functionality

Co-authored-by: Michael Scire <SciresM@gmail.com>
2020-03-08 00:06:23 -08:00
Michael Scire
f9403201f0 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "83aa6133"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "83aa6133"
git-subrepo:
  version:  "0.4.1"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "a04d8c2"
2020-03-02 20:00:49 -08:00
Michael Scire
fd34e2342a ams: bump version to 0.10.3 2020-03-02 20:00:10 -08:00
Michael Scire
0e5ade727c git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "807c6866"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "807c6866"
git-subrepo:
  version:  "0.4.1"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "a04d8c2"
2020-03-02 19:52:45 -08:00
Michael Scire
c4065c976c pm/fusee: add support for 9.2.0 2020-03-02 19:49:34 -08:00
Michael Scire
392c9c3a0b ams: prepare for 0.10.3 2020-03-02 18:35:10 -08:00
TuxSH
4f33afe0ee vapours: fix MASKL, MASK2L to match Armv8 ARM (inclusive bit range) 2020-03-01 15:16:12 +00:00
Michael Scire
dc1404061c fs.mitm: implement layered html redirection (closes #814) 2020-02-25 16:44:36 -08:00
Michael Scire
8da705d40b fs.mitm: support html redirection (non-layered, for #814) 2020-02-25 06:41:53 -08:00
Michael Scire
4f34a38a97 Add additional licensing exemption 2020-02-24 20:47:11 -08:00
Michael Scire
799f94958c util/lmem: minor fixes 2020-02-24 20:28:39 -08:00
Michael Scire
c17b6d0d44 util: add uuid 2020-02-24 20:14:48 -08:00
SciresM
5a8aebf74d lmem: Implement UnitHeap, ExpHeap (#823)
* lmem: implement ExpHeap

* lmem: Implement Slab^H^H^H^HUnitHeap
2020-02-24 19:44:27 -08:00
Michael Scire
191414c832 util: add ConvertTo*Endian48 2020-02-24 19:29:00 -08:00
Michael Scire
a429c61f33 crypto/spl: implement rsa-oaep 2020-02-24 19:09:13 -08:00
Michael Scire
ad1158b30a ro: save 0x25000 while maintaining security improvements 2020-02-24 18:17:33 -08:00
Michael Scire
ba8c77fec6 ro: implement 9.1.0 security measures 2020-02-24 18:02:01 -08:00
Michael Scire
f427c3d7a5 ro: move in use trackers outside of info objects 2020-02-24 16:47:07 -08:00
Michael Scire
05a24b5726 boot: fix more typos/copy paste errors 2020-02-24 09:49:43 -08:00
Michael Scire
8306464758 boot: fix wrong fallthroughs 2020-02-24 09:45:40 -08:00
Michael Scire
4a30437e80 boot: update to accurate as of 9.1.0 2020-02-24 08:21:31 -08:00
Michael Scire
80e1847534 libstrat: fix unconverted _WITH_ASSERTs 2020-02-24 05:01:41 -08:00
Michael Scire
8ccac1d18a pss: extra validity check 2020-02-24 04:32:37 -08:00
Michael Scire
c3656aae30 crypto: constant-time pss (if it survives optimization) 2020-02-24 02:27:01 -08:00
Michael Scire
5fa534fb71 ro: implement NRR signature checks 2020-02-23 18:07:14 -08:00
Michael Scire
f3629f863d crypto: implement RSA-2048-PSS 2020-02-23 17:34:30 -08:00
Michael Scire
d675aa3414 crypto: Implement BigNum/ExpMod 2020-02-23 06:51:32 -08:00
Michael Scire
981166783b stratosphere: misc static -> global 2020-02-22 23:16:53 -08:00
Michael Scire
c0f7cd95b9 pm: save ~0x3000 in binary 2020-02-22 23:11:28 -08:00
Michael Scire
40400aee1f ams: revamp assertion system 2020-02-22 23:05:14 -08:00
Michael Scire
9572fb2ce3 vapours: cleanup svc includes 2020-02-22 19:54:50 -08:00
Michael Scire
eb75d54b2b vapours: new include style, add crypto 2020-02-22 19:47:08 -08:00
Michael Scire
081bd0aefc fix building libstratosphere 2020-02-22 19:21:26 -08:00
SciresM
fa4a96d021 Merge pull request #821 from Atmosphere-NX/mesosphere-dev
Mesosphere: Implement kernel initialization.
2020-02-22 19:07:30 -08:00
SciresM
3c5efefb15 Merge branch 'master' into mesosphere-dev 2020-02-22 19:07:05 -08:00
Michael Scire
3e4c962bbb kern: correct error in KUserPointer 2020-02-22 15:38:31 -08:00
Michael Scire
34fb48b412 kern: mem access prep for svc streams, TODO_IMPLEMENT -> UNIMPLEMENTED 2020-02-22 05:42:46 -08:00
Michael Scire
40980904f7 kern: paranoid assertions about the class tokens 2020-02-22 03:04:31 -08:00
Michael Scire
5055bdb23e kern: correct KClassToken generation 2020-02-22 02:53:14 -08:00
Michael Scire
d2a888d9e8 kern: minor fixes to prevent core0-2 panics during boot 2020-02-21 13:05:16 -08:00
Michael Scire
bb3f7c8ab9 kern: fix building without debugging turned on 2020-02-20 10:14:27 -08:00
Michael Scire
efae01c165 kern: implement KUserPointer (and test with QueryMemory) in advance of svc dev 2020-02-20 09:05:24 -08:00
Michael Scire
9f9593e05f kern: implement large page group mapping, fix debug flag cap 2020-02-19 22:35:31 -08:00
Michael Scire
80b115ef57 kern: implement user thread start, el0 code now runs 2020-02-19 21:21:44 -08:00
Michael Scire
560c340c96 kern: fix supervisor page table activation 2020-02-19 20:43:19 -08:00
Michael Scire
aae3c789f2 kern: implement unsuspension of init threads 2020-02-19 20:42:21 -08:00
Michael Scire
c568788609 kern: implement KProcess::Run 2020-02-19 19:38:20 -08:00
Michael Scire
28ea0b12a8 kern: implement SetProcessMemoryPermission 2020-02-19 08:07:44 -08:00
Michael Scire
b857153964 kern: finish KProcess::Initialize() for KIPs 2020-02-19 06:46:59 -08:00
Michael Scire
d9c3908caf kern: mark initial threads as intended constinit 2020-02-19 05:45:32 -08:00
Michael Scire
b99bcbc619 kern: implement MapIo 2020-02-19 05:35:22 -08:00
Michael Scire
905ce0eeea kern: implement capabilities parsing 2020-02-19 04:55:00 -08:00
Michael Scire
0534ddd37a kern: correct alloc region size 2020-02-19 01:26:16 -08:00
Michael Scire
30b3825f5c libmesosphere: precompile mesosphere.hpp 2020-02-19 01:22:51 -08:00
Michael Scire
05a3e95834 kern: implement process(pagetable) init 2020-02-19 01:22:27 -08:00
Michael Scire
fba8fb539d kern: add KProcess members 2020-02-18 05:04:49 -08:00
Michael Scire
772e1f1c4f kern: implement IsValidPageGroup 2020-02-18 01:44:40 -08:00
Michael Scire
154422562a kern: implement page group unmapping 2020-02-18 01:04:44 -08:00
Michael Scire
25b0baae59 kern: change decompression asserts -> audits 2020-02-17 02:57:01 -08:00
Michael Scire
92521eed2a kern: implement through kip decompression 2020-02-17 02:49:21 -08:00
Michael Scire
cbc73f4407 kern: skeleton initial process loading 2020-02-15 03:44:41 -08:00
Michael Scire
30d6b359f9 kern: implement smmu init 2020-02-15 00:00:35 -08:00
Michael Scire
2c496e94d5 kern: switch->nx, implement sleep manager init 2020-02-14 19:58:57 -08:00
Michael Scire
20b5268e90 kern: kern::arm64 -> kern::arch::arm64 2020-02-14 18:22:55 -08:00
Michael Scire
11f70759ce kern: implement lightlock slowpath (works on hw) 2020-02-14 15:27:34 -08:00
Michael Scire
b3e6571586 kern: improve panic output 2020-02-14 04:01:09 -08:00
Michael Scire
5b98426171 kern: implement KWorkerTaskManager 2020-02-14 02:56:42 -08:00
Michael Scire
c91386b0fa kern: implement interrupt thread init 2020-02-14 02:20:33 -08:00
Michael Scire
19e6d2e1c0 kernel_ldr: fix consistency on hardware 2020-02-13 22:53:26 -08:00
Michael Scire
ea0011d572 kern: use TinyMT instead of mt19937_t 2020-02-13 22:37:30 -08:00
Michael Scire
364b04b68a libmesosphere: move .s to kernel (prevent manifesting in kernelldr) 2020-02-13 22:05:20 -08:00
Michael Scire
8c93eb5712 kern: implement enough of KPageTable to initialize a thread 2020-02-13 17:38:56 -08:00
Michael Scire
c6d1579265 vapours: add time primitives, fix result name 2020-02-13 16:18:39 -08:00
hexkyz
34bb800440 fusee/sept: restore and further match official behavior for SDMMC 2020-02-11 17:13:01 +00:00
Michael Scire
ef3da6cb51 kern: implement KMemoryBlockManager::Update 2020-02-10 09:57:00 -08:00
Michael Scire
3bcc4adb5c kern: implement init through InterruptTaskManager.Initialize() 2020-02-10 02:26:00 -08:00
Michael Scire
3c78bc4dbf kern: implement KMemoryBlockManager init 2020-02-10 01:50:23 -08:00
Michael Scire
fdd7b1db15 kern: add KPageTableBase members 2020-02-09 18:29:38 -08:00
Michael Scire
50b8189e7f kern: begin skeletoning page table types 2020-02-09 03:45:45 -08:00
Michael Scire
3284656aad kern: print build date/time, git revision 2020-02-09 02:05:49 -08:00
Michael Scire
1d65352167 kern: print layout for debug during startup 2020-02-09 01:16:13 -08:00
Michael Scire
d8e94996ad kern: newlines at end of svc files 2020-02-08 11:56:13 -08:00
Michael Scire
5dae43b192 kern: add svc stubs, turn on svc table build 2020-02-08 11:53:27 -08:00
Michael Scire
4b6b06fcaa mesosphere: hard enforcement of TODOs 2020-02-08 03:18:08 -08:00
Michael Scire
919b8124dc kern: Implement exception vector ASM 2020-02-08 02:49:32 -08:00
Michael Scire
e330b6187f kern: bugfix, turn on auditing for the time being 2020-02-07 19:48:07 -08:00
Michael Scire
1224ed8abe kern: implement dpc + skeleton rest of main 2020-02-07 19:16:09 -08:00
Michael Scire
e9e949ec36 kern: obj stubs to get past slab init 2020-02-07 11:51:58 -08:00
Michael Scire
8cfffc69d5 kern: print backtrace on panic 2020-02-07 06:26:01 -08:00
Michael Scire
cb6af379d8 kern: implement dynamic slab init + ini relocation 2020-02-07 04:58:35 -08:00
Michael Scire
d9e6771e63 kern: implement more of KMemoryManager through KPageBuffer slab init 2020-02-06 20:36:26 -08:00
Michael Scire
f7d3d50f33 kern: implement KMemoryManager init 2020-02-06 17:40:57 -08:00
hexkyz
9dc7a4dc18 fusee/sept: don't abort on SDMMC DMA transfer failures 2020-02-06 19:15:13 +00:00
Michael Scire
1de607c183 kern: implement KResourceLimit 2020-02-06 05:34:38 -08:00
Michael Scire
23f5d77f37 kern: fix carveout region calcs 2020-02-06 01:22:21 -08:00
Michael Scire
5961151a92 kern: implement uart init + logging 2020-02-06 01:05:35 -08:00
Michael Scire
323858cf96 kern: s/MemoryBlock/MemoryRegion/g 2020-02-05 14:16:56 -08:00
Michael Scire
5f857cb079 kern: implement more of KInterruptManager 2020-02-05 14:07:51 -08:00
Michael Scire
62de3322ff kern: implement much of KScheduler, KHardwareTimer 2020-02-05 13:02:35 -08:00
Michael Scire
5e4307046a style cleanup 2020-01-31 16:38:33 -08:00
Michael Scire
2355047715 kern: add abstract scoped lock template 2020-01-31 16:37:58 -08:00
Michael Scire
57222e8301 kern: implement KSchedulerLock 2020-01-31 16:25:17 -08:00
Michael Scire
797c04d19f kern .fini array: one weird trick 2020-01-31 04:57:28 -08:00
Michael Scire
9accbcf8e1 meso: use -fno-non-call-exceptions, -fno-use-cxa-atexit 2020-01-31 04:47:08 -08:00
Michael Scire
86eea62cd8 kernel: use .rel.ro 2020-01-31 03:37:49 -08:00
Michael Scire
00b093ec62 util: remove spurious error in parent offset calc 2020-01-31 02:03:34 -08:00
Michael Scire
b2b1129cc0 kern: mostly implement KThread::Initialize 2020-01-31 01:53:30 -08:00
Michael Scire
d9db723bc8 kern: add all kthread members 2020-01-31 00:07:06 -08:00
Michael Scire
08cb370a45 kern: implement KThreadContext 2020-01-30 22:46:18 -08:00
Michael Scire
d262ff92cc kern: implement KPriorityQueue 2020-01-30 20:56:24 -08:00
Michael Scire
e1adbb6dba util: add BitSet 2020-01-30 17:45:54 -08:00
Michael Scire
e25a4ca8d7 kern: implement KAffinityMask 2020-01-30 17:07:08 -08:00
Michael Scire
059c706f19 kern: implement KThreadLocalPage 2020-01-30 16:51:35 -08:00
Michael Scire
484f132651 kern: implement KHandleTable, other cleanup 2020-01-30 15:29:51 -08:00
Michael Scire
d5a4c17ee7 kern: fully implement slabheap init 2020-01-30 01:41:59 -08:00
Michael Scire
7d6b16d7fb kern: Skeleton KSynchronizationObject 2020-01-29 23:46:55 -08:00
Michael Scire
2faf3d33b5 kern: Implement KAutoObject, KSlabHeap, KLightLock 2020-01-29 22:06:25 -08:00
Michael Scire
bb4ade30e4 kern: optimize memory block tree lookups 2020-01-29 14:55:26 -08:00
Michael Scire
981bb1f15d kern: skeleton enough types to init KCoreLocalRegion in main() 2020-01-29 14:26:24 -08:00
Michael Scire
ad0d2faa6c meso: makefile improvements, sysreg accessors 2020-01-29 05:37:14 -08:00
Michael Scire
507ab46709 cpu: optimize core barrier 2020-01-29 04:36:18 -08:00
Michael Scire
7820e5b759 kern: implement debug register/vectors init 2020-01-29 02:42:41 -08:00
Michael Scire
7c703903ea kern: implement pool partition memblock setup 2020-01-29 01:49:04 -08:00
Michael Scire
e7dee2a9fc kern: Implement most of memory init (all cores hit main, but still more to do) 2020-01-28 22:09:47 -08:00
Michael Scire
b2e522c0a0 meso: find -exec sed -i'' -e 's/2018-2019 Atmo/2018-2020 Atmo/g' {} + 2020-01-24 02:12:37 -08:00
Michael Scire
875b62f06a config: fix typos 2020-01-24 02:11:27 -08:00
Michael Scire
32fb22e361 kern: finish implementing assembly init routines 2020-01-24 02:11:26 -08:00
Michael Scire
b3bfd6c4c9 meso: discard .interp section 2020-01-24 02:11:25 -08:00
Michael Scire
866771fdae kern: reuse data cache code during init 2020-01-24 02:11:25 -08:00
Michael Scire
308ddecc9c kern: fix bss end align (now gets through kernelldr on hardware) 2020-01-24 02:11:25 -08:00
Michael Scire
24d41ce55e kern: push code through call to kernelldr 2020-01-24 02:11:25 -08:00
Michael Scire
bce7133128 meso: add script to build mesosphere.bin 2020-01-24 02:11:24 -08:00
Michael Scire
3982afdd6b meso: skeleton buildsystem for kernel 2020-01-24 02:11:23 -08:00
Michael Scire
6ee305464a mesosphere: Implement SVC table auto-generation 2020-01-24 02:11:21 -08:00
1137 changed files with 92401 additions and 6669 deletions

1
.gitignore vendored
View File

@@ -64,6 +64,7 @@ dkms.conf
# Distribution files
*.tgz
*.zip
*.bz2
# IDA binaries
*.id0

View File

@@ -28,6 +28,7 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -18,3 +18,5 @@ enable_user_pmu_access = 0
[stratosphere]
; To force-enable nogc, add nogc = 1
; To force-disable nogc, add nogc = 0
; To opt in to using Atmosphere's NCM reimplementation, add enable_ncm = 1

View File

@@ -1,4 +1,52 @@
# Changelog
## 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.
+ This caused games that have particularly enormous file metadata tables (> 4 MB) to exhaust fs.mitm's 16 MB memory pool.
+ The code that creates romfs images was thus changed to be significantly more memory efficient, again.
+ Memory requirements have been lowered from ~4x file table size to ~2x file table size + 0.5 MB.
+ There is a slight speed penalty to this, but testing on Football Manager 2020 only took an extra ~1.5 seconds for the game to boot with many modded files.
+ This shouldn't be noticeable thanks to the async changes made in 0.10.2.
+ If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact SciresM.
+ 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.
+ This also lays the groundwork for libstratosphere providing bindings for changing the installed version of the Switch's OS.
+ **Please Note**: The NCM implementation will initially be opt-in.
+ The Atmosphere team is confident in our NCM implementation (and we have tested it on every firmware version).
+ That said, this is our first system module that manages NAND savegames -- and caution is a habit.
+ We do not anticipate any issues that didn't come up in testing, so this is just our being particularly careful.
+ Users interested in opting in to using our implementation should set `stratosphere!ncm_enabled = 1` in BCT.ini.
+ In the unlikely event that any issues are encountered, please report them to @SciresM.
+ The NCM implementation will stop being opt-in in a future update, after thorough testing has been done in practice.
+ A bug was fixed in emummc that caused Nintendo path to be corrupted on 1.0.0.
+ This manifested as the emummc folder being created inside the virtual NAND instead of the SD card.
+ It's unlikely there are any negative consequences to this in practice.
+ If you want to be truly sure, you can re-clone sysmmc before updating a 1.0.0 emummc to latest firmware.
+ Stratosphere system modules now use new Nintendo-style FS bindings instead of stdio.
+ This saves a modest amount of memory due to leaner code, and greatly increases the accuracy of several components.
+ These bindings will make it easier for other system modules using libstratosphere to interact with the filesystem.
+ This also lays the groundwork for changes necessary to support per-emummc Atmosphere folders in a future update.
+ 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.
+ This works like normal layeredfs, replacing content placed in `/atmosphere/contents/<program id>/manual_html/`.
+ This allows for game mods/translations to provide custom manual content, if they so choose.
+ A number of improvements were made to Atmosphere's memory usage, including:
+ `fatal` now uses STB instead of freetype for rendering.
+ This saves around 1 MB of memory, and makes our fatal substantially leaner than Nintendo's.
+ `sm` no longer wastes 2 MiB unnecessarily.
+ fusee/sept's sdmmc access now better matches official behavior.
+ This improves compatibility with some SD cards.
+ `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.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emuMMC
branch = develop
commit = bd81a674a946c30b566e1732a95c18f19b701558
parent = 6ee525201ccef107c61d81ba73c891e3eb5f0215
commit = d12dd5464422029a1e5601916517ec3f1c81d8d0
parent = 259a1a7513236a1de4d373bc6cb99032ede2c626
method = rebase
cmdver = 0.4.0
cmdver = 0.4.1

View File

@@ -1,21 +1,21 @@
# emuMMC
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
### Supported Horizon Versions
**1.0.0 - 9.1.0**
## Features
* Arbitrary SDMMC backend selection
* Arbitrary SDMMC backend selection
**This allows loading eMMC from SD or even SD from eMMC**
* On the fly hooking / patching, fully self-infesting
* On the fly hooking / patching, fully self-infesting
**Only one payload required for all versions!**
* File-based SDMMC backend support (from SD)
* File-based SDMMC backend support (from SD)
**This allows loading eMMC images from hekate-backups (split or not)**
* SDMMC device based sector offset (*currently eMMC only*)
* SDMMC device based sector offset (*currently eMMC only*)
**Raw partition support for eMMC from SD with less performance overhead**
* Full support for `/Nintendo` folder redirection to a arbitrary path
* Full support for `/Nintendo` folder redirection to a arbitrary path
**No 8 char length restriction!**
* exosphere based context configuration
* exosphere based context configuration
**This includes full support for multiple emuMMC images**
## Compiling

View File

@@ -48,10 +48,8 @@
// Nintendo Paths
#define FS_OFFSET_100_NINTENDO_PATHS \
{ \
{.opcode_reg = 9, .adrp_offset = 0x00032C58, .add_rel_offset = 4}, \
{.opcode_reg = 8, .adrp_offset = 0x00032C60, .add_rel_offset = 4}, \
{.opcode_reg = 9, .adrp_offset = 0x00032F3C, .add_rel_offset = 4}, \
{.opcode_reg = 8, .adrp_offset = 0x00032F44, .add_rel_offset = 4}, \
{.opcode_reg = 8, .adrp_offset = 0x00032C58, .add_rel_offset = 8}, \
{.opcode_reg = 9, .adrp_offset = 0x00032F40, .add_rel_offset = 8}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}

View File

@@ -48,6 +48,9 @@ extern char __argdata__;
// TODO
static char nintendo_path[0x80] = "Nintendo";
// 1.0.0 requires special path handling because it has separate album and contents paths.
#define FS_100_ALBUM_PATH 0
#define FS_100_CONTENTS_PATH 1
static char nintendo_path_album_100[0x100] = "/Nintendo/Album";
static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents";
@@ -275,23 +278,18 @@ void setup_nintendo_paths(void)
// 1.0.0 needs special handling because it uses two paths.
// Do album path
{
int path_len = snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100;
intptr_t album_path_location = nintendo_album_path_location + path_len - 6; // "/Album"
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[0].adrp_offset);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[1].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[0].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[0].add_rel_offset, nintendo_album_path_location);
write_adrp_add(fs_offsets->nintendo_paths[1].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[1].add_rel_offset, album_path_location);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].add_rel_offset, nintendo_album_path_location);
}
// Do contents path
{
int path_len = snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100;
intptr_t contents_path_location = nintendo_contents_path_location + path_len - 9; // "/Contents"
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[2].adrp_offset);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[3].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[2].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[2].add_rel_offset, nintendo_contents_path_location);
write_adrp_add(fs_offsets->nintendo_paths[3].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[3].add_rel_offset, contents_path_location);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].add_rel_offset, nintendo_contents_path_location);
}
}
}

View File

@@ -21,12 +21,12 @@
static inline void uart_wait_cycles(uint32_t baud, uint32_t num)
{
wait((num * 1000000 + 16 * baud - 1) / (16 * baud));
udelay((num * 1000000 + 16 * baud - 1) / (16 * baud));
}
static inline void uart_wait_syms(uint32_t baud, uint32_t num)
{
wait((num * 1000000 + baud - 1) / baud);
udelay((num * 1000000 + baud - 1) / baud);
}
void uart_config(UartDevice dev) {
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
switch (dev) {
case UART_A:
pinmux->uart1_tx = 0;
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart1_rts = 0;
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart2_rts = 0;
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart3_rts = 0;
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart4_rts = 0;
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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. */

View File

@@ -21,66 +21,58 @@
#include "gpio.h"
#include "utils.h"
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
/* Set GPIO's value. */
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
return &gpio->bank[bank_number];
}
static volatile uint32_t gpio_get_port(uint32_t pin) {
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
}
static volatile uint32_t gpio_get_mask(uint32_t pin) {
uint32_t pin_number = (pin & GPIO_PIN_MASK);
return (1 << pin_number);
}
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
/* Set or clear the bit, as appropriate. */
if (should_be_set)
if (do_set)
cluster[port] |= mask;
else
cluster[port] &= ~mask;
/* Dummy read. */
(void)cluster[port];
cluster[port];
}
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
/* Get GPIO's value. */
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
/* Retrieve the register set that corresponds to the given pin and offset. */
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
/* Convert the given value to a boolean. */
return !!(cluster[port] & mask);
}
/* Configure GPIO's mode. */
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
}
/* Configure GPIO's direction. */
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
}
/* Write to GPIO. */
void gpio_write(uint32_t pin, uint32_t value) {
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
}
/* Read from GPIO. */
uint32_t gpio_read(uint32_t pin) {
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
}

View File

@@ -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 "panic.h"
#include "di.h"
@@ -51,8 +51,10 @@ static const char *get_error_desc_str(uint32_t error_desc) {
static void _check_and_display_atmosphere_fatal_error(void) {
/* Check for valid magic. */
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0) {
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0)
{
return;
}
@@ -69,10 +71,10 @@ static void _check_and_display_atmosphere_fatal_error(void) {
/* Turn on the backlight after initializing the lfb */
/* to avoid flickering. */
display_backlight(true);
/* Override the global logging level. */
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
/* Copy fatal error context to the stack. */
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
@@ -103,7 +105,7 @@ static void _check_and_display_atmosphere_fatal_error(void) {
void check_and_display_panic(void) {
/* Handle a panic sent via a stratosphere module. */
_check_and_display_atmosphere_fatal_error();
/* We also handle our own panics. */
bool has_panic = ((APBDEV_PMC_RST_STATUS_0 != 0) || (g_panic_code != 0));
uint32_t code = (g_panic_code == 0) ? APBDEV_PMC_SCRATCH200_0 : g_panic_code;
@@ -152,10 +154,10 @@ void check_and_display_panic(void) {
/* Initialize the display. */
display_init();
/* Fill the screen. */
display_color_screen(color);
/* Wait for button and reboot. */
wait_for_button_and_reboot();
} else {

View File

@@ -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_PANIC_H
#define FUSEE_PANIC_H
@@ -30,6 +30,7 @@
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
#define AMS_FATAL_ERROR_TLS_SIZE 0x100
/* Atmosphere reboot-to-fatal-error. */
typedef struct {
@@ -57,10 +58,13 @@ typedef struct {
uint64_t stack_dump_size;
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
uint8_t tls[AMS_FATAL_ERROR_TLS_SIZE];
} atmosphere_fatal_error_ctx;
/* "AFE2" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
/* "AFE1" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 0x31454641
/* "AFE0" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641

View File

@@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
/* Ensure we haven't timed out. */
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
sdmmc_error(sdmmc, "Auto-calibration timed out!");
sdmmc_warn(sdmmc, "Auto-calibration timed out!");
/* Force a register read to refresh the clock control value. */
sdmmc_get_sd_clock_control(sdmmc);
@@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc)
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
{
/* Set all error bits and enable the relevant interrupts. */
sdmmc->regs->int_enable |= 0x017F0000;
/* Enable the relevant interrupts and set all error bits. */
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
sdmmc->regs->int_enable |= 0x017F0000;
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
@@ -1324,34 +1324,35 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc)
static void sdmmc_intr_disable(sdmmc_t *sdmmc)
{
/* Clear all error bits and the interrupts. */
/* Clear all error bits and disable the relevant interrupts. */
sdmmc->regs->int_enable &= ~(0x017F0000);
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
}
static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask)
static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask)
{
bool is_masked = (sdmmc->regs->int_status & status_mask);
uint32_t int_status = sdmmc->regs->int_status;
/* Mask status. */
if (is_masked)
sdmmc->regs->int_status &= status_mask;
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
return is_masked;
}
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
{
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
/* Return the status, if necessary. */
if (status_out)
*status_out = (int_status & 0xFFFF);
/* Refresh status. */
if (is_error)
sdmmc->regs->int_status = sdmmc->regs->int_status;
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
{
/* Acknowledge error by refreshing status. */
sdmmc->regs->int_status = int_status;
return -1;
}
else if (int_status & status_mask)
{
/* Mask the status. */
sdmmc->regs->int_status = (int_status & status_mask);
return 1;
}
return is_error;
return 0;
}
static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
@@ -1442,15 +1443,23 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
/* Watch over the DMA transfer. */
while (!is_timeout)
{
/* Check interrupts. */
uint16_t intr_status = 0;
int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT);
/* An error has been raised. Reset. */
if (sdmmc_intr_check_error(sdmmc))
if (intr_res < 0)
{
sdmmc_do_sw_reset(sdmmc);
return 0;
}
/* Transfer is over. */
if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE)
return 1;
/* We have a DMA interrupt. Restart the transfer where it was interrupted. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT))
if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)
{
if (sdmmc->use_adma)
{
@@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
sdmmc->next_dma_addr += 0x80000;
}
/* Transfer is over. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE))
return 1;
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
}
@@ -1526,12 +1531,15 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc)
/* Wait for CMD to finish. */
while (!is_err && !is_timeout) {
/* Check interrupts. */
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
/* Command is done. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
if (intr_res > 0)
return 1;
/* Check for any raised errors. */
is_err = sdmmc_intr_check_error(sdmmc);
is_err = (intr_res < 0);
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
@@ -1642,6 +1650,7 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
is_dma = true;
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
/* Abort in case initialization failed. */
if (!dma_blkcnt)
{
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
@@ -1669,12 +1678,14 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
/* Save response, if necessary. */
sdmmc_save_response(sdmmc, cmd->flags);
/* Process the DMA request. */
/* Update the DMA request. */
if (req)
{
/* Disable interrupts and abort in case updating failed. */
if (!sdmmc_dma_update(sdmmc))
{
sdmmc_error(sdmmc, "Failed to process the DMA transfer!");
sdmmc_warn(sdmmc, "Failed to update the DMA transfer!");
sdmmc_intr_disable(sdmmc);
return 0;
}
@@ -1840,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
while (!is_timeout)
{
/* Buffer Read Ready was asserted. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY))
if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0)
{
/* Manually disable the Buffer Read Ready interrupt. */
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);

View File

@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
switch (dev) {
case UART_A:
pinmux->uart1_tx = 0;
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart1_rts = 0;
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart2_rts = 0;
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart3_rts = 0;
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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;
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart4_rts = 0;
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
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. */

View File

@@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
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 \
@@ -96,7 +96,7 @@ 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)))
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
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 \
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
@@ -221,12 +221,12 @@ sept_secondary_01.enc.o sept_secondary_01_enc.h: sept-secondary_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)

View File

@@ -230,6 +230,8 @@ SECTIONS
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(__ncm_kip_start__ = ncm_kip - __start__);
PROVIDE(__ncm_kip_size__ = ncm_kip_end - ncm_kip);
PROVIDE(__pm_kip_start__ = pm_kip - __start__);
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);

View File

@@ -21,66 +21,58 @@
#include "gpio.h"
#include "utils.h"
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
/* Set GPIO's value. */
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
return &gpio->bank[bank_number];
}
static volatile uint32_t gpio_get_port(uint32_t pin) {
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
}
static volatile uint32_t gpio_get_mask(uint32_t pin) {
uint32_t pin_number = (pin & GPIO_PIN_MASK);
return (1 << pin_number);
}
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
/* Set or clear the bit, as appropriate. */
if (should_be_set)
if (do_set)
cluster[port] |= mask;
else
cluster[port] &= ~mask;
/* Dummy read. */
(void)cluster[port];
cluster[port];
}
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
/* Get GPIO's value. */
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
/* Retrieve the register set that corresponds to the given pin and offset. */
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
/* Convert the given value to a boolean. */
return !!(cluster[port] & mask);
}
/* Configure GPIO's mode. */
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
}
/* Configure GPIO's direction. */
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
}
/* Write to GPIO. */
void gpio_write(uint32_t pin, uint32_t value) {
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
}
/* Read from GPIO. */
uint32_t gpio_read(uint32_t pin) {
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
}

View File

@@ -801,6 +801,16 @@ static const kernel_info_t g_kernel_infos[] = {
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x65780,
KERNEL_PATCHES(900)
},
{ /* 9.2.0. */
/* NOTE: 9.2.0 has identical kernel layout to 9.0.0, so patches may be reused. */
.hash = {0x66, 0xE7, 0x73, 0xE7, 0xF5, 0xCB, 0x9B, 0xB8, 0x66, 0xB7, 0xB1, 0x26, 0x23, 0x02, 0x76, 0xF2, 0xD1, 0x60, 0x3E, 0x09, 0x14, 0x19, 0xC2, 0x84, 0xFA, 0x5D, 0x0F, 0x16, 0xA3, 0x65, 0xFA, 0x17},
.hash_offset = 0x1C0,
.hash_size = 0x90000 - 0x1C0,
.embedded_ini_offset = 0x90000,
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x65780,
KERNEL_PATCHES(900)
}
};

View File

@@ -184,6 +184,12 @@ static int stratosphere_ini_handler(void *user, const char *section, const char
strat_cfg->has_nogc_config = true;
sscanf(value, "%d", &tmp);
strat_cfg->enable_nogc = tmp != 0;
} else if (strcmp(name, STRATOSPHERE_ENABLE_NCM_KEY) == 0) {
sscanf(value, "%d", &tmp);
strat_cfg->ncm_enabled = tmp != 0;
if (strat_cfg->ncm_enabled) {
stratosphere_enable_ncm();
}
} else {
return 0;
}

View File

@@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
/* Ensure we haven't timed out. */
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
sdmmc_error(sdmmc, "Auto-calibration timed out!");
sdmmc_warn(sdmmc, "Auto-calibration timed out!");
/* Force a register read to refresh the clock control value. */
sdmmc_get_sd_clock_control(sdmmc);
@@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc)
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
{
/* Set all error bits and enable the relevant interrupts. */
sdmmc->regs->int_enable |= 0x017F0000;
/* Enable the relevant interrupts and set all error bits. */
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
sdmmc->regs->int_enable |= 0x017F0000;
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
@@ -1324,34 +1324,35 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc)
static void sdmmc_intr_disable(sdmmc_t *sdmmc)
{
/* Clear all error bits and the interrupts. */
/* Clear all error bits and disable the relevant interrupts. */
sdmmc->regs->int_enable &= ~(0x017F0000);
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
}
static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask)
static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask)
{
bool is_masked = (sdmmc->regs->int_status & status_mask);
uint32_t int_status = sdmmc->regs->int_status;
/* Mask status. */
if (is_masked)
sdmmc->regs->int_status &= status_mask;
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
return is_masked;
}
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
{
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
/* Return the status, if necessary. */
if (status_out)
*status_out = (int_status & 0xFFFF);
/* Refresh status. */
if (is_error)
sdmmc->regs->int_status = sdmmc->regs->int_status;
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
{
/* Acknowledge error by refreshing status. */
sdmmc->regs->int_status = int_status;
return -1;
}
else if (int_status & status_mask)
{
/* Mask the status. */
sdmmc->regs->int_status = (int_status & status_mask);
return 1;
}
return is_error;
return 0;
}
static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
@@ -1442,15 +1443,23 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
/* Watch over the DMA transfer. */
while (!is_timeout)
{
/* Check interrupts. */
uint16_t intr_status = 0;
int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT);
/* An error has been raised. Reset. */
if (sdmmc_intr_check_error(sdmmc))
if (intr_res < 0)
{
sdmmc_do_sw_reset(sdmmc);
return 0;
}
/* Transfer is over. */
if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE)
return 1;
/* We have a DMA interrupt. Restart the transfer where it was interrupted. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT))
if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)
{
if (sdmmc->use_adma)
{
@@ -1467,10 +1476,6 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
sdmmc->next_dma_addr += 0x80000;
}
/* Transfer is over. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE))
return 1;
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
}
@@ -1526,12 +1531,15 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc)
/* Wait for CMD to finish. */
while (!is_err && !is_timeout) {
/* Check interrupts. */
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
/* Command is done. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
if (intr_res > 0)
return 1;
/* Check for any raised errors. */
is_err = sdmmc_intr_check_error(sdmmc);
is_err = (intr_res < 0);
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
@@ -1642,6 +1650,7 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
is_dma = true;
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
/* Abort in case initialization failed. */
if (!dma_blkcnt)
{
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
@@ -1669,12 +1678,14 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
/* Save response, if necessary. */
sdmmc_save_response(sdmmc, cmd->flags);
/* Process the DMA request. */
/* Update the DMA request. */
if (req)
{
/* Disable interrupts and abort in case updating failed. */
if (!sdmmc_dma_update(sdmmc))
{
sdmmc_error(sdmmc, "Failed to process the DMA transfer!");
sdmmc_warn(sdmmc, "Failed to update the DMA transfer!");
sdmmc_intr_disable(sdmmc);
return 0;
}
@@ -1840,7 +1851,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
while (!is_timeout)
{
/* Buffer Read Ready was asserted. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY))
if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0)
{
/* Manually disable the Buffer Read Ready interrupt. */
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);

View File

@@ -97,11 +97,18 @@ _metadata:
#define CONTENT_TYPE_KLD 9
#define CONTENT_TYPE_KRN 10
#define CONTENT_FLAG_NONE (0 << 0)
#define CONTENT_FLAG0_EXPERIMENTAL (1 << 0)
_content_headers:
/* ams_mitm content header */
.word __ams_mitm_kip_start__
.word __ams_mitm_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "ams_mitm"
.align 5
@@ -109,7 +116,10 @@ _content_headers:
/* boot content header */
.word __boot_kip_start__
.word __boot_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "boot"
.align 5
@@ -117,7 +127,10 @@ _content_headers:
/* exosphere content header */
.word __exosphere_bin_start__
.word __exosphere_bin_size__
.word CONTENT_TYPE_EXO
.byte CONTENT_TYPE_EXO
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "exosphere"
.align 5
@@ -125,7 +138,10 @@ _content_headers:
/* fusee_primary content header */
.word __fusee_primary_bin_start__
.word __fusee_primary_bin_size__
.word CONTENT_TYPE_FSP
.byte CONTENT_TYPE_FSP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "fusee_primary"
.align 5
@@ -133,15 +149,21 @@ _content_headers:
/* loader content header */
.word __loader_kip_start__
.word __loader_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "loader"
.asciz "Loader"
.align 5
/* lp0fw content header */
.word __lp0fw_bin_start__
.word __lp0fw_bin_size__
.word CONTENT_TYPE_WBT
.byte CONTENT_TYPE_WBT
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "lp0fw"
.align 5
@@ -149,15 +171,21 @@ _content_headers:
/* pm content header */
.word __pm_kip_start__
.word __pm_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "pm"
.asciz "ProcessManager"
.align 5
/* rebootstub content header */
.word __rebootstub_bin_start__
.word __rebootstub_bin_size__
.word CONTENT_TYPE_RBT
.byte CONTENT_TYPE_RBT
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "rebootstub"
.align 5
@@ -165,7 +193,10 @@ _content_headers:
/* sept_primary content header */
.word __sept_primary_bin_start__
.word __sept_primary_bin_size__
.word CONTENT_TYPE_SP1
.byte CONTENT_TYPE_SP1
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "sept_primary"
.align 5
@@ -173,7 +204,10 @@ _content_headers:
/* sept_secondary 00 content header */
.word __sept_secondary_00_enc_start__
.word __sept_secondary_00_enc_size__
.word CONTENT_TYPE_SP2
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "septsecondary00"
.align 5
@@ -181,7 +215,10 @@ _content_headers:
/* sept_secondary 01 content header */
.word __sept_secondary_01_enc_start__
.word __sept_secondary_01_enc_size__
.word CONTENT_TYPE_SP2
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "septsecondary01"
.align 5
@@ -189,7 +226,10 @@ _content_headers:
/* sm content header */
.word __sm_kip_start__
.word __sm_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "sm"
.align 5
@@ -197,15 +237,32 @@ _content_headers:
/* spl content header */
.word __spl_kip_start__
.word __spl_kip_size__
.word CONTENT_TYPE_KIP
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "spl"
.align 5
/* ncm content header */
.word __ncm_kip_start__
.word __ncm_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG0_EXPERIMENTAL
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "NCM"
.align 5
/* emummc content header */
.word __emummc_kip_start__
.word __emummc_kip_size__
.word CONTENT_TYPE_EMC
.byte CONTENT_TYPE_EMC
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "emummc"
.align 5
@@ -213,7 +270,10 @@ _content_headers:
/* kernel_ldr content header */
.word __kernel_ldr_bin_start__
.word __kernel_ldr_bin_size__
.word CONTENT_TYPE_KLD
.byte CONTENT_TYPE_KLD
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "kernel_ldr"
.align 5
@@ -221,7 +281,10 @@ _content_headers:
/* splash_screen content header */
.word __splash_screen_bmp_start__
.word __splash_screen_bmp_size__
.word CONTENT_TYPE_BMP
.byte CONTENT_TYPE_BMP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "splash_screen"
.align 5

View File

@@ -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 <stdint.h>
@@ -30,6 +30,7 @@
#define u8 uint8_t
#define u32 uint32_t
#include "loader_kip.h"
#include "ncm_kip.h"
#include "pm_kip.h"
#include "sm_kip.h"
#include "ams_mitm_kip.h"
@@ -47,9 +48,10 @@ static bool g_stratosphere_pm_enabled = true;
static bool g_stratosphere_ams_mitm_enabled = true;
static bool g_stratosphere_spl_enabled = true;
static bool g_stratosphere_boot_enabled = true;
static bool g_stratosphere_ncm_enabled = false;
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ncm_kip[], ams_mitm_kip[];
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ncm_kip_size, ams_mitm_kip_size;
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
@@ -57,6 +59,19 @@ emummc_fs_ver_t stratosphere_get_fs_version(void) {
return g_fs_ver;
}
void stratosphere_enable_ncm(void) {
/* The Atmosphere team believes our implementation of NCM to be extremely accurate, */
/* and does not think it likely there is any possibility of undesirable behavior */
/* when using the NCM reimplementation. However, because NCM manages critical save games */
/* the implementation will default to off for some time, until the code has been thoroughly */
/* tested in practice. */
/* PLEASE NOTE: The default behavior will be NCM on in a future atmosphere release, */
/* and this opt-in functionality will be removed at that time. */
g_stratosphere_ncm_enabled = true;
}
/* GCC doesn't consider the size as const... we have to write it ourselves. */
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
@@ -68,38 +83,43 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
}
size_t size = sizeof(ini1_header_t);
/* Calculate our processes' sizes. */
if (g_stratosphere_loader_enabled) {
size += loader_kip_size;
num_processes++;
}
if (g_stratosphere_pm_enabled) {
size += pm_kip_size;
num_processes++;
}
if (g_stratosphere_sm_enabled) {
size += sm_kip_size;
num_processes++;
}
if (g_stratosphere_spl_enabled) {
size += spl_kip_size;
num_processes++;
}
if (g_stratosphere_ams_mitm_enabled) {
size += ams_mitm_kip_size;
num_processes++;
}
if (g_stratosphere_ncm_enabled) {
size += ncm_kip_size;
num_processes++;
}
if (g_stratosphere_boot_enabled) {
size += boot_kip_size;
num_processes++;
}
g_stratosphere_ini1 = (ini1_header_t *)malloc(size);
if (g_stratosphere_ini1 == NULL) {
@@ -134,6 +154,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
data += spl_kip_size;
}
if (g_stratosphere_ncm_enabled) {
memcpy(data, ncm_kip, ncm_kip_size);
data += ncm_kip_size;
}
if (g_stratosphere_ams_mitm_enabled) {
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
data += ams_mitm_kip_size;
@@ -143,7 +168,7 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
memcpy(data, boot_kip, boot_kip_size);
data += boot_kip_size;
}
return g_stratosphere_ini1;
}
@@ -160,26 +185,26 @@ void stratosphere_free_ini1(void) {
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
size_t file_size = get_file_size(kip_path);
if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
}
kip1_header_t kip_header;
if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
return;
}
size_t kip_size = kip1_get_size_from_header(&kip_header);
if (kip_size > file_size) {
fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
}
if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
/* TODO: is this error fatal? */
return;
}
ini1->size += kip_size;
ini1->num_processes++;
}
@@ -189,24 +214,24 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
size_t fs_kip_size, emummc_kip_size;
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
/* Allocate kip. */
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
if (injected_kip == NULL) {
fatal_error("Failed to allocate memory for emummc kip injection!");
}
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
if (emummc_data_size & 0xFFF) {
fatal_error("Invalid emummc kip!");
}
/* Copy over capabilities. */
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
/* Add extra cap for 1.0.0 */
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
for (size_t i = 0; i < 0x20; i++) {
@@ -217,14 +242,14 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
}
}
}
/* Update sections. */
injected_kip->section_headers[0].out_size += emummc_data_size;
injected_kip->section_headers[0].compressed_size += emummc_data_size;
for (size_t i = 1; i < 4; i++) {
injected_kip->section_headers[i].out_offset += emummc_data_size;
}
/* Copy data. */
{
size_t ofs = 0;
@@ -234,22 +259,22 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
}
}
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
return injected_kip;
}
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (g_sd_files_ini1 != NULL) {
return g_sd_files_ini1;
}
/* Allocate space. */
g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
g_sd_files_ini1->magic = MAGIC_INI1;
g_sd_files_ini1->size = sizeof(ini1_header_t);
g_sd_files_ini1->num_processes = 0;
g_sd_files_ini1->_0xC = 0;
/* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
DIR *kips_dir = opendir("atmosphere/kips");
struct dirent *ent;
@@ -258,15 +283,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
}
char kip_path[0x301] = {0};
snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
struct stat kip_stat;
if (stat(kip_path, &kip_stat) == -1) {
continue;
}
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
/* If file, add to ini1. */
try_add_sd_kip(g_sd_files_ini1, kip_path);
@@ -279,15 +304,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
continue;
}
/* Reuse kip path variable. */
memset(kip_path, 0, sizeof(kip_path));
snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
if (stat(kip_path, &kip_stat) == -1) {
continue;
}
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
/* If file, add to ini1. */
try_add_sd_kip(g_sd_files_ini1, kip_path);
@@ -299,7 +324,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
}
closedir(kips_dir);
}
return g_sd_files_ini1;
}
@@ -344,7 +369,7 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
}
offset += kip1_get_size_from_header(current_kip);
bool already_loaded = false;
for (uint32_t j = 0; j < merged->num_processes; j++) {
if (process_list[j] == current_kip->title_id) {
@@ -355,20 +380,20 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
if (already_loaded) {
continue;
}
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
size_t current_kip_size = kip1_get_size_from_header(current_kip);
if (current_kip_size > remaining_size) {
fatal_error("Not enough space for all the KIP1s!\n");
}
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
}
if (patched_kip != NULL) {
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
if (patched_kip_size > remaining_size) {
@@ -377,9 +402,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
memcpy(current_dst_kip, patched_kip, patched_kip_size);
remaining_size -= patched_kip_size;
current_dst_kip += patched_kip_size;
free(patched_kip);
} else {
} else {
memcpy(current_dst_kip, current_kip, current_kip_size);
remaining_size -= current_kip_size;
current_dst_kip += current_kip_size;

View File

@@ -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_STRATOSPHERE_H
#define FUSEE_STRATOSPHERE_H
@@ -30,6 +30,8 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
ini1_header_t *stratosphere_get_sd_files_ini1(void);
void stratosphere_free_ini1(void);
void stratosphere_enable_ncm(void);
emummc_fs_ver_t stratosphere_get_fs_version(void);
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
@@ -37,8 +39,10 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_in
typedef struct {
bool has_nogc_config;
bool enable_nogc;
bool ncm_enabled;
} stratosphere_cfg_t;
#define STRATOSPHERE_NOGC_KEY "nogc"
#define STRATOSPHERE_ENABLE_NCM_KEY "enable_ncm"
#endif

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 08c9b3cbf85471fe6adb5f42ba9f03357a5f633a
parent = 814c9d1cfb54c192bc1deb140ab476c9f3dd0edf
commit = da6eac986d7f67ca3dcc3a8df0c191ffbea4686f
parent = eb48e7cc5943bd594b3166cb888d3dadb0474107
method = merge
cmdver = 0.4.0
cmdver = 0.4.1

View File

@@ -11,6 +11,7 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -5,7 +5,7 @@ endif
include $(DEVKITPRO)/devkitA64/base_rules
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM64
export ATMOSPHERE_SETTINGS += -march=armv8-a -mtp=soft
export ATMOSPHERE_SETTINGS += -march=armv8-a+crc+crypto -mtp=soft
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View File

@@ -1,4 +1,4 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_SWITCH -D__SWITCH__
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_NX -D__SWITCH__
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=

View File

@@ -24,11 +24,11 @@ export ATMOSPHERE_ASFLAGS :=
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
export ATMOSPHERE_ARCH_DIR := arch/arm64
export ATMOSPHERE_BOARD_DIR := board/nintendo/switch
export ATMOSPHERE_BOARD_DIR := board/nintendo/nx
export ATMOSPHERE_OS_DIR := os/horizon
export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := nintendo_switch
export ATMOSPHERE_BOARD_NAME := nintendo_nx
export ATMOSPHERE_OS_NAME := horizon
endif
@@ -76,14 +76,17 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
DATA := data
INCLUDES := include
SOURCES ?= $(foreach d,$(filter-out source/arch source/board,$(wildcard source)),$(call DIR_WILDCARD,$d) $d)
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_ARCH_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_ARCH_DIR))
endif
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_BOARD_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_BOARD_DIR))
endif
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_OS_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_OS_DIR))
endif
GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,))
SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/.*),$1/$2 $(call DIR_WILDCARD,$1/$2),)
ALL_SOURCE_DIRS=$(call GENERAL_SOURCE_DIRS,$1) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_ARCH_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_BOARD_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_OS_DIR))
SOURCES ?= $(call ALL_SOURCE_DIRS,source)
#---------------------------------------------------------------------------------
# Rules for compiling pre-compiled headers
#---------------------------------------------------------------------------------
%.gch: %.hpp
@echo $<
$(CXX) -w -x c++-header -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
@cp $@ $(<).gch

View File

@@ -7,12 +7,12 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk
# options for code generation
#---------------------------------------------------------------------------------
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export LDFLAGS = -specs=$(TOPDIR)/kernel_ldr.specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map)
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 \
@@ -25,7 +25,6 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_call_terminate \
-Wl,--wrap,__gxx_personality_v0 \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_ZSt19__throw_logic_errorPKc \
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
-Wl,--wrap,_ZNSt11logic_errorC2EPKc

View File

@@ -15,11 +15,11 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export ASFLAGS = $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \
@@ -36,17 +36,17 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
export LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
export LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
export LIBS := -lstratosphere -lnx
export LIBS = -lstratosphere -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
export LIBDIRS := $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
export LIBDIRS = $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
#---------------------------------------------------------------------------------
# stratosphere sysmodules may (but usually do not) have an exefs source dir
#---------------------------------------------------------------------------------
export EXEFS_SRC := exefs_src
export EXEFS_SRC = exefs_src

View File

@@ -6,12 +6,17 @@ 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 :=
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -flto
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source)
LIBS :=
#---------------------------------------------------------------------------------
@@ -27,7 +32,7 @@ LIBDIRS := $(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)), \
@@ -64,6 +69,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)))
@@ -105,13 +111,15 @@ clean:
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(filter-out kern_svc_tables.o, $(OFILES)) : $(GCH_FILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------

View File

@@ -11,6 +11,7 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -19,20 +19,70 @@
#include <vapours.hpp>
/* First, pull in core macros (panic, etc). */
#include "mesosphere/kern_panic.hpp"
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_panic.hpp>
/* Primitive types. */
#include "mesosphere/kern_k_typed_address.hpp"
#include "mesosphere/kern_initial_process.hpp"
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_initial_process.hpp>
#include <mesosphere/kern_k_exception_context.hpp>
/* Core pre-initialization includes. */
#include "mesosphere/kern_select_cpu.hpp"
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_select_system_control.hpp>
#include <mesosphere/kern_k_target_system.hpp>
/* Initialization headers. */
#include "mesosphere/init/kern_init_elf.hpp"
#include "mesosphere/init/kern_init_layout.hpp"
#include "mesosphere/init/kern_init_page_table_select.hpp"
#include <mesosphere/init/kern_init_elf.hpp>
#include <mesosphere/init/kern_init_layout.hpp>
#include <mesosphere/init/kern_init_slab_setup.hpp>
#include <mesosphere/init/kern_init_page_table_select.hpp>
#include <mesosphere/init/kern_init_arguments_select.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
/* Core functionality. */
#include "mesosphere/kern_select_interrupts.hpp"
#include "mesosphere/kern_select_k_system_control.hpp"
#include <mesosphere/kern_select_interrupt_manager.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_memory_manager.hpp>
#include <mesosphere/kern_k_interrupt_task_manager.hpp>
#include <mesosphere/kern_k_core_local_region.hpp>
#include <mesosphere/kern_k_slab_heap.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
#include <mesosphere/kern_k_dpc_manager.hpp>
#include <mesosphere/kern_kernel.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
#include <mesosphere/kern_select_page_table.hpp>
/* Miscellaneous objects. */
#include <mesosphere/kern_k_shared_memory_info.hpp>
#include <mesosphere/kern_k_event_info.hpp>
/* Auto Objects. */
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_readable_event.hpp>
#include <mesosphere/kern_k_handle_table.hpp>
#include <mesosphere/kern_k_event.hpp>
#include <mesosphere/kern_k_interrupt_event.hpp>
#include <mesosphere/kern_k_light_session.hpp>
#include <mesosphere/kern_k_session.hpp>
#include <mesosphere/kern_k_session_request.hpp>
#include <mesosphere/kern_k_port.hpp>
#include <mesosphere/kern_k_shared_memory.hpp>
#include <mesosphere/kern_k_transfer_memory.hpp>
#include <mesosphere/kern_k_code_memory.hpp>
#include <mesosphere/kern_k_device_address_space.hpp>
#include <mesosphere/kern_select_debug.hpp>
#include <mesosphere/kern_k_process.hpp>
#include <mesosphere/kern_k_resource_limit.hpp>
#include <mesosphere/kern_k_synchronization.hpp>
/* More Miscellaneous objects. */
#include <mesosphere/kern_k_object_name.hpp>
#include <mesosphere/kern_k_scoped_resource_reservation.hpp>
/* Supervisor Calls. */
#include <mesosphere/kern_svc.hpp>
/* Main functionality. */
#include <mesosphere/kern_main.hpp>

View File

@@ -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
namespace ams::kern::init {
struct KInitArguments {
u64 ttbr0;
u64 ttbr1;
u64 tcr;
u64 mair;
u64 cpuactlr;
u64 cpuectlr;
u64 sctlr;
u64 sp;
u64 entrypoint;
u64 argument;
u64 setup_function;
};
}

View File

@@ -15,173 +15,12 @@
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_panic.hpp>
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include "../kern_cpu.hpp"
namespace ams::kern::init {
constexpr size_t PageSize = 0x1000;
constexpr size_t L1BlockSize = 0x40000000;
constexpr size_t L2BlockSize = 0x200000;
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
constexpr size_t L3BlockSize = 0x1000;
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
class PageTableEntry {
public:
enum Permission : u64 {
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
};
enum Shareable : u64 {
Shareable_NonShareable = (0 << 8),
Shareable_OuterShareable = (2 << 8),
Shareable_InnerShareable = (3 << 8),
};
/* Official attributes are: */
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
enum PageAttribute : u64 {
PageAttribute_Device_nGnRnE = (0 << 2),
PageAttribute_Device_nGnRE = (1 << 2),
PageAttribute_NormalMemory = (2 << 2),
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
};
enum AccessFlag : u64 {
AccessFlag_NotAccessed = (0 << 10),
AccessFlag_Accessed = (1 << 10),
};
protected:
u64 attributes;
public:
/* Take in a raw attribute. */
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
/* Extend a previous attribute. */
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
/* Construct a new attribute. */
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share))
{
/* ... */
}
protected:
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->attributes >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
return this->attributes & (((1ul << count) - 1) << offset);
}
public:
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; }
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
/* Should not be called except by derived classes. */
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
return this->attributes;
}
};
static_assert(sizeof(PageTableEntry) == sizeof(u64));
constexpr PageTableEntry InvalidPageTableEntry = PageTableEntry(0);
constexpr size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
class L1PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(30, 18);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L2PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(21, 27);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L3PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
namespace ams::kern::arch::arm64::init {
class KInitialPageTable {
public:
@@ -190,10 +29,14 @@ namespace ams::kern::init {
virtual KPhysicalAddress Allocate() { return Null<KPhysicalAddress>; }
virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); }
};
struct NoClear{};
private:
KPhysicalAddress l1_table;
public:
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : l1_table(l1) {
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1, NoClear) : l1_table(l1) { /* ... */ }
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : KInitialPageTable(l1, NoClear{}) {
ClearNewPageTable(this->l1_table);
}
@@ -224,9 +67,9 @@ namespace ams::kern::init {
public:
void NOINLINE Map(KVirtualAddress virt_addr, size_t size, KPhysicalAddress phys_addr, const PageTableEntry &attr, IPageAllocator &allocator) {
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
/* Iteratively map pages until the requested region is mapped. */
while (size > 0) {
@@ -309,10 +152,37 @@ namespace ams::kern::init {
}
}
KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const {
/* Get the L1 entry. */
const L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
if (l1_entry->IsBlock()) {
return l1_entry->GetBlock() + (GetInteger(virt_addr) & (L1BlockSize - 1));
}
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
/* Get the L2 entry. */
const L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
return l2_entry->GetBlock() + (GetInteger(virt_addr) & (L2BlockSize - 1));
}
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* Get the L3 entry. */
const L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
return l3_entry->GetBlock() + (GetInteger(virt_addr) & (L3BlockSize - 1));
}
bool IsFree(KVirtualAddress virt_addr, size_t size) {
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
const KVirtualAddress end_virt_addr = virt_addr + size;
while (virt_addr < end_virt_addr) {
@@ -360,8 +230,8 @@ namespace ams::kern::init {
cpu::DataSynchronizationBarrierInnerShareable();
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
/* Iteratively reprotect pages until the requested region is reprotected. */
while (size > 0) {
@@ -371,9 +241,9 @@ namespace ams::kern::init {
if (l1_entry->IsBlock()) {
/* Ensure that we are allowed to have an L1 block here. */
const KPhysicalAddress block = l1_entry->GetBlock();
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L1BlockSize));
MESOSPHERE_ABORT_UNLESS(l1_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L1 block. */
*static_cast<PageTableEntry *>(l1_entry) = InvalidPageTableEntry;
@@ -389,7 +259,7 @@ namespace ams::kern::init {
}
/* Not a block, so we must be a table. */
MESOSPHERE_ABORT_UNLESS(l1_entry->IsTable());
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
@@ -397,14 +267,14 @@ namespace ams::kern::init {
if (l2_entry->IsContiguous()) {
/* Ensure that we are allowed to have a contiguous L2 block here. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2ContiguousBlockSize));
/* Invalidate the existing contiguous L2 block. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
/* Ensure that the entry is valid. */
MESOSPHERE_ABORT_UNLESS(l2_entry[i].IsCompatibleWithAttribute(attr_before, true));
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry[i].IsCompatibleWithAttribute(attr_before, true));
static_cast<PageTableEntry *>(l2_entry)[i] = InvalidPageTableEntry;
}
cpu::DataSynchronizationBarrierInnerShareable();
@@ -419,10 +289,10 @@ namespace ams::kern::init {
size -= L2ContiguousBlockSize;
} else {
/* Ensure that we are allowed to have an L2 block here. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L2BlockSize));
MESOSPHERE_ABORT_UNLESS(l2_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L2 block. */
*static_cast<PageTableEntry *>(l2_entry) = InvalidPageTableEntry;
@@ -440,23 +310,23 @@ namespace ams::kern::init {
}
/* Not a block, so we must be a table. */
MESOSPHERE_ABORT_UNLESS(l2_entry->IsTable());
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* We must have a mapped l3 entry to reprotect. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
MESOSPHERE_ABORT_UNLESS(l3_entry->IsBlock());
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
const KPhysicalAddress block = l3_entry->GetBlock();
if (l3_entry->IsContiguous()) {
/* Ensure that we are allowed to have a contiguous L3 block here. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3ContiguousBlockSize));
/* Invalidate the existing contiguous L3 block. */
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
/* Ensure that the entry is valid. */
MESOSPHERE_ABORT_UNLESS(l3_entry[i].IsCompatibleWithAttribute(attr_before, true));
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry[i].IsCompatibleWithAttribute(attr_before, true));
static_cast<PageTableEntry *>(l3_entry)[i] = InvalidPageTableEntry;
}
cpu::DataSynchronizationBarrierInnerShareable();
@@ -471,10 +341,10 @@ namespace ams::kern::init {
size -= L3ContiguousBlockSize;
} else {
/* Ensure that we are allowed to have an L3 block here. */
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L3BlockSize));
MESOSPHERE_ABORT_UNLESS(l3_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L3 block. */
*static_cast<PageTableEntry *>(l3_entry) = InvalidPageTableEntry;
@@ -495,4 +365,35 @@ namespace ams::kern::init {
};
class KInitialPageAllocator : public KInitialPageTable::IPageAllocator {
private:
uintptr_t next_address;
public:
constexpr ALWAYS_INLINE KInitialPageAllocator() : next_address(Null<uintptr_t>) { /* ... */ }
ALWAYS_INLINE void Initialize(uintptr_t address) {
this->next_address = address;
}
ALWAYS_INLINE uintptr_t GetFinalNextAddress() {
const uintptr_t final_address = this->next_address;
this->next_address = Null<uintptr_t>;
return final_address;
}
ALWAYS_INLINE uintptr_t GetFinalState() {
return this->GetFinalNextAddress();
}
public:
virtual KPhysicalAddress Allocate() override {
MESOSPHERE_INIT_ABORT_UNLESS(this->next_address != Null<uintptr_t>);
const uintptr_t allocated = this->next_address;
this->next_address += PageSize;
std::memset(reinterpret_cast<void *>(allocated), 0, PageSize);
return allocated;
}
/* No need to override free. The default does nothing, and so would we. */
};
}

View File

@@ -15,9 +15,27 @@
*/
#pragma once
#include <vapours.hpp>
#include "kern_cpu_system_registers.hpp"
#include <mesosphere/arch/arm64/kern_cpu_system_registers.hpp>
#include <mesosphere/kern_select_userspace_memory_access.hpp>
namespace ams::kern::arm64::cpu {
namespace ams::kern::arch::arm64::cpu {
#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
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
constexpr inline size_t NumCores = 4;
#else
#error "Unknown Board for cpu::NumCores"
#endif
/* Initialization. */
NOINLINE void InitializeInterruptThreads(s32 core_id);
/* Helpers for managing memory state. */
ALWAYS_INLINE void DataSynchronizationBarrier() {
@@ -46,13 +64,154 @@ namespace ams::kern::arm64::cpu {
EnsureInstructionConsistency();
}
ALWAYS_INLINE void SwitchProcess(u64 ttbr, u32 proc_id) {
SetTtbr0El1(ttbr);
ContextIdRegisterAccessor(0).SetProcId(proc_id).Store();
InstructionMemoryBarrier();
}
/* Performance counter helpers. */
ALWAYS_INLINE u64 GetCycleCounter() {
return cpu::GetPmcCntrEl0();
}
ALWAYS_INLINE u32 GetPerformanceCounter(s32 n) {
u64 counter = 0;
if (n < static_cast<s32>(NumPerformanceCounters)) {
switch (n) {
case 0:
counter = cpu::GetPmevCntr0El0();
break;
case 1:
counter = cpu::GetPmevCntr1El0();
break;
case 2:
counter = cpu::GetPmevCntr2El0();
break;
case 3:
counter = cpu::GetPmevCntr3El0();
break;
case 4:
counter = cpu::GetPmevCntr4El0();
break;
case 5:
counter = cpu::GetPmevCntr5El0();
break;
default:
break;
}
}
return static_cast<u32>(counter);
}
/* Helper for address access. */
ALWAYS_INLINE bool GetPhysicalAddressWritable(KPhysicalAddress *out, KVirtualAddress addr, bool privileged = false) {
const uintptr_t va = GetInteger(addr);
if (privileged) {
__asm__ __volatile__("at s1e1w, %[va]" :: [va]"r"(va) : "memory");
} else {
__asm__ __volatile__("at s1e0w, %[va]" :: [va]"r"(va) : "memory");
}
InstructionMemoryBarrier();
u64 par = GetParEl1();
if (par & 0x1) {
return false;
}
if (out) {
*out = KPhysicalAddress((par & 0xFFFFFFFFF000ull) | (va & 0xFFFull));
}
return true;
}
ALWAYS_INLINE bool GetPhysicalAddressReadable(KPhysicalAddress *out, KVirtualAddress addr, bool privileged = false) {
const uintptr_t va = GetInteger(addr);
if (privileged) {
__asm__ __volatile__("at s1e1r, %[va]" :: [va]"r"(va) : "memory");
} else {
__asm__ __volatile__("at s1e0r, %[va]" :: [va]"r"(va) : "memory");
}
InstructionMemoryBarrier();
u64 par = GetParEl1();
if (par & 0x1) {
return false;
}
if (out) {
*out = KPhysicalAddress((par & 0xFFFFFFFFF000ull) | (va & 0xFFFull));
}
return true;
}
/* Synchronization helpers. */
NOINLINE void SynchronizeAllCores();
/* Cache management helpers. */
void FlushEntireDataCacheShared();
void FlushEntireDataCacheLocal();
void ClearPageToZeroImpl(void *);
void FlushEntireDataCacheSharedForInit();
void FlushEntireDataCacheLocalForInit();
void FlushEntireDataCache();
Result InvalidateDataCache(void *addr, size_t size);
Result StoreDataCache(const void *addr, size_t size);
Result FlushDataCache(const void *addr, size_t size);
Result InvalidateInstructionCache(void *addr, size_t size);
ALWAYS_INLINE void ClearPageToZero(void *page) {
MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
MESOSPHERE_ASSERT(page != nullptr);
ClearPageToZeroImpl(page);
}
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
const u64 value = (static_cast<u64>(asid) << 48);
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(static_cast<u64>(value) << 48) : "memory");
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateTlbByAsidAndVa(u32 asid, KProcessAddress virt_addr) {
const u64 value = (static_cast<u64>(asid) << 48) | ((GetInteger(virt_addr) >> 12) & 0xFFFFFFFFFFFul);
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(value) : "memory");
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateEntireTlb() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
DataSynchronizationBarrier();
}
ALWAYS_INLINE void InvalidateTlbByVaDataOnly(KProcessAddress virt_addr) {
const u64 value = ((GetInteger(virt_addr) >> 12) & 0xFFFFFFFFFFFul);
__asm__ __volatile__("tlbi vaae1is, %[value]" :: [value]"r"(value) : "memory");
DataSynchronizationBarrier();
}
ALWAYS_INLINE uintptr_t GetCoreLocalRegionAddress() {
register uintptr_t x18 asm("x18");
__asm__ __volatile__("" : [x18]"=r"(x18));
return x18;
}
ALWAYS_INLINE void SetCoreLocalRegionAddress(uintptr_t value) {
register uintptr_t x18 asm("x18") = value;
__asm__ __volatile__("":: [x18]"r"(x18));
SetTpidrEl1(value);
}
ALWAYS_INLINE void SwitchThreadLocalRegion(uintptr_t tlr) {
cpu::SetTpidrRoEl0(tlr);
}
}

View File

@@ -16,7 +16,7 @@
#pragma once
#include <vapours.hpp>
namespace ams::kern::arm64::cpu {
namespace ams::kern::arch::arm64::cpu {
#define MESOSPHERE_CPU_GET_SYSREG(name) \
({ \
@@ -37,8 +37,14 @@ namespace ams::kern::arm64::cpu {
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Ttbr0El1, ttbr0_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Ttbr1El1, ttbr1_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(MairEl1, mair_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TcrEl1, tcr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(MairEl1, mair_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrEl1, tpidr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(VbarEl1, vbar_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(FarEl1, far_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(ParEl1, par_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(SctlrEl1, sctlr_el1)
@@ -46,21 +52,215 @@ namespace ams::kern::arm64::cpu {
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CpuEctlrEl1, s3_1_c15_c2_1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CsselrEl1, csselr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CcsidrEl1, ccsidr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(OslarEl1, oslar_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrRoEl0, tpidrro_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(EsrEl1, esr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Afsr0El1, afsr0_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Afsr1El1, afsr1_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmUserEnrEl0, pmuserenr_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmcCntrEl0, pmccntr_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr0El0, pmevcntr0_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr1El0, pmevcntr1_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr2El0, pmevcntr2_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr3El0, pmevcntr3_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr4El0, pmevcntr4_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr5El0, pmevcntr5_el0)
#define FOR_I_IN_0_TO_15(HANDLER, ...) \
HANDLER(0, ## __VA_ARGS__) HANDLER(1, ## __VA_ARGS__) HANDLER(2, ## __VA_ARGS__) HANDLER(3, ## __VA_ARGS__) \
HANDLER(4, ## __VA_ARGS__) HANDLER(5, ## __VA_ARGS__) HANDLER(6, ## __VA_ARGS__) HANDLER(7, ## __VA_ARGS__) \
HANDLER(8, ## __VA_ARGS__) HANDLER(9, ## __VA_ARGS__) HANDLER(10, ## __VA_ARGS__) HANDLER(11, ## __VA_ARGS__) \
HANDLER(12, ## __VA_ARGS__) HANDLER(13, ## __VA_ARGS__) HANDLER(14, ## __VA_ARGS__) HANDLER(15, ## __VA_ARGS__) \
#define MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS(ID, ...) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgWcr##ID##El1, dbgwcr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgWvr##ID##El1, dbgwvr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgBcr##ID##El1, dbgbcr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgBvr##ID##El1, dbgbvr##ID##_el1)
FOR_I_IN_0_TO_15(MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS)
#undef MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS
/* Base class for register accessors. */
class GenericRegisterAccessor {
class GenericRegisterAccessorBase {
NON_COPYABLE(GenericRegisterAccessorBase);
NON_MOVEABLE(GenericRegisterAccessorBase);
private:
u64 value;
public:
ALWAYS_INLINE GenericRegisterAccessor(u64 v) : value(v) { /* ... */ }
constexpr ALWAYS_INLINE GenericRegisterAccessorBase(u64 v) : value(v) { /* ... */ }
protected:
constexpr ALWAYS_INLINE u64 GetValue() const {
return this->value;
}
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->value >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->value &= ~mask;
this->value |= (value & (mask >> offset)) << offset;
}
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->value &= ~mask;
this->value |= (value & mask);
}
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
const u64 mask = 1ul << offset;
if (enabled) {
this->value |= mask;
} else {
this->value &= ~mask;
}
}
};
/* Special code for main id register. */
class MainIdRegisterAccessor : public GenericRegisterAccessor {
template<typename Derived>
class GenericRegisterAccessor : public GenericRegisterAccessorBase {
public:
constexpr ALWAYS_INLINE GenericRegisterAccessor(u64 v) : GenericRegisterAccessorBase(v) { /* ... */ }
protected:
ALWAYS_INLINE void Store() const {
static_cast<const Derived *>(this)->Store();
}
};
#define MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(name) class name##RegisterAccessor : public GenericRegisterAccessor<name##RegisterAccessor>
#define MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(accessor, reg_name) \
ALWAYS_INLINE accessor##RegisterAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(reg_name)) { /* ... */ } \
constexpr ALWAYS_INLINE accessor##RegisterAccessor(u64 v) : GenericRegisterAccessor(v) { /* ... */ } \
\
ALWAYS_INLINE void Store() { const u64 v = this->GetValue(); MESOSPHERE_CPU_SET_SYSREG(reg_name, v); }
/* Accessors. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MemoryAccessIndirection) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MemoryAccessIndirection, mair_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(TranslationControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(TranslationControl, tcr_el1)
constexpr ALWAYS_INLINE size_t GetT1Size() const {
const size_t shift_value = this->GetBits(16, 6);
return size_t(1) << (size_t(64) - shift_value);
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ArchitecturalFeatureAccessControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ArchitecturalFeatureAccessControl, cpacr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetFpEnabled(bool en) {
if (en) {
this->SetBits(20, 2, 0x3);
} else {
this->SetBits(20, 2, 0x0);
}
return *this;
}
constexpr ALWAYS_INLINE bool IsFpEnabled() {
return this->GetBits(20, 2) != 0;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(DebugFeature) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(DebugFeature, id_aa64dfr0_el1)
constexpr ALWAYS_INLINE size_t GetNumWatchpoints() const {
return this->GetBits(20, 4);
}
constexpr ALWAYS_INLINE size_t GetNumBreakpoints() const {
return this->GetBits(12, 4);
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MonitorDebugSystemControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MonitorDebugSystemControl, mdscr_el1)
constexpr ALWAYS_INLINE bool GetMde() const {
return this->GetBits(15, 1) != 0;
}
constexpr ALWAYS_INLINE size_t GetTdcc() const {
return this->GetBits(12, 1) != 0;
}
constexpr ALWAYS_INLINE decltype(auto) SetMde(bool set) {
this->SetBit(15, set);
return *this;
}
constexpr ALWAYS_INLINE decltype(auto) SetTdcc(bool set) {
this->SetBit(12, set);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MultiprocessorAffinity) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MultiprocessorAffinity, mpidr_el1)
constexpr ALWAYS_INLINE u64 GetAff0() const {
return this->GetBits(0, 8);
}
constexpr ALWAYS_INLINE u64 GetAff1() const {
return this->GetBits(8, 8);
}
constexpr ALWAYS_INLINE u64 GetAff2() const {
return this->GetBits(16, 8);
}
constexpr ALWAYS_INLINE u64 GetAff3() const {
return this->GetBits(32, 8);
}
constexpr ALWAYS_INLINE u64 GetCpuOnArgument() const {
constexpr u64 Mask = 0x000000FF00FFFF00ul;
return this->GetValue() & Mask;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ThreadId) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ThreadId, tpidr_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(OsLockAccess) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(OsLockAccess, oslar_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ContextId) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ContextId, contextidr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetProcId(u32 proc_id) {
this->SetBits(0, BITSIZEOF(proc_id), proc_id);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MainId) {
public:
enum class Implementer {
ArmLimited = 0x41,
@@ -70,7 +270,7 @@ namespace ams::kern::arm64::cpu {
CortexA57 = 0xD07,
};
public:
ALWAYS_INLINE MainIdRegisterAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(midr_el1)) { /* ... */ }
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MainId, midr_el1)
public:
constexpr ALWAYS_INLINE Implementer GetImplementer() const {
return static_cast<Implementer>(this->GetBits(24, 8));
@@ -93,10 +293,69 @@ namespace ams::kern::arm64::cpu {
}
};
/* Accessors for cache registers. */
class CacheLineIdAccessor : public GenericRegisterAccessor {
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(SystemControl) {
public:
ALWAYS_INLINE CacheLineIdAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(clidr_el1)) { /* ... */ }
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(SystemControl, sctlr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetWxn(bool en) {
this->SetBit(19, en);
return *this;
}
};
/* Accessors for timer registers. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerKernelControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerKernelControl, cntkctl_el1)
constexpr ALWAYS_INLINE decltype(auto) SetEl0PctEn(bool en) {
this->SetBit(0, en);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalTimerControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalTimerControl, cntp_ctl_el0)
constexpr ALWAYS_INLINE decltype(auto) SetEnable(bool en) {
this->SetBit(0, en);
return *this;
}
constexpr ALWAYS_INLINE decltype(auto) SetIMask(bool en) {
this->SetBit(1, en);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalTimerCompareValue) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalTimerCompareValue, cntp_cval_el0)
constexpr ALWAYS_INLINE u64 GetCompareValue() {
return this->GetValue();
}
constexpr ALWAYS_INLINE decltype(auto) SetCompareValue(u64 value) {
this->SetBits(0, BITSIZEOF(value), value);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalCountValue) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalCountValue, cntpct_el0)
constexpr ALWAYS_INLINE u64 GetCount() {
return this->GetValue();
}
};
/* Accessors for cache registers. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CacheLineId) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CacheLineId, clidr_el1)
public:
constexpr ALWAYS_INLINE int GetLevelsOfCoherency() const {
return static_cast<int>(this->GetBits(24, 3));
@@ -109,9 +368,9 @@ namespace ams::kern::arm64::cpu {
/* TODO: Other bitfield accessors? */
};
class CacheSizeIdAccessor : public GenericRegisterAccessor {
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CacheSizeId) {
public:
ALWAYS_INLINE CacheSizeIdAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(ccsidr_el1)) { /* ... */ }
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CacheSizeId, ccsidr_el1)
public:
constexpr ALWAYS_INLINE int GetNumberOfSets() const {
return static_cast<int>(this->GetBits(13, 15));
@@ -128,6 +387,9 @@ namespace ams::kern::arm64::cpu {
/* TODO: Other bitfield accessors? */
};
#undef FOR_I_IN_0_TO_15
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS
#undef MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS
#undef MESOSPHERE_CPU_GET_SYSREG
#undef MESOSPHERE_CPU_SET_SYSREG

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_debug_base.hpp>
namespace ams::kern {
class KThread;
class KProcess;
}
namespace ams::kern::arch::arm64 {
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KDebugBase> {
MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject);
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
namespace ams::kern::arch::arm64 {
struct KExceptionContext {
u64 x[(30 - 0) + 1];
u64 sp;
u64 pc;
u64 psr;
u64 tpidr;
u64 reserved;
};
static_assert(sizeof(KExceptionContext) == 0x120);
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_hardware_timer_base.hpp>
namespace ams::kern::arch::arm64 {
namespace impl {
class KHardwareTimerInterruptTask;
}
class KHardwareTimer : public KHardwareTimerBase {
public:
constexpr KHardwareTimer() : KHardwareTimerBase() { /* ... */ }
public:
/* Public API. */
NOINLINE void Initialize(s32 core_id);
NOINLINE void Finalize();
static s64 GetTick() {
return GetCount();
}
void RegisterAbsoluteTask(KTimerTask *task, s64 task_time) {
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->GetLock());
if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
SetCompareValue(task_time);
EnableInterrupt();
}
}
private:
friend class impl::KHardwareTimerInterruptTask;
NOINLINE void DoInterruptTask();
private:
/* Hardware register accessors. */
static ALWAYS_INLINE void InitializeGlobalTimer() {
/* Set kernel control. */
cpu::CounterTimerKernelControlRegisterAccessor(0).SetEl0PctEn(true).Store();
/* Disable the physical timer. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store();
/* Set the compare value to the maximum. */
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits<u64>::max()).Store();
/* Enable the physical timer, with interrupt masked. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store();
}
static ALWAYS_INLINE void EnableInterrupt() {
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(false).Store();
}
static ALWAYS_INLINE void DisableInterrupt() {
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store();
}
static ALWAYS_INLINE void StopTimer() {
/* Set the compare value to the maximum. */
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits<u64>::max()).Store();
/* Disable the physical timer. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store();
}
static ALWAYS_INLINE s64 GetCount() {
return cpu::CounterTimerPhysicalCountValueRegisterAccessor().GetCount();
}
static ALWAYS_INLINE void SetCompareValue(s64 value) {
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(static_cast<u64>(value)).Store();
}
};
}

View File

@@ -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>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern::arch::arm64 {
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(std::is_pod<GicDistributor>::value);
static_assert(sizeof(GicDistributor) == 0x1000);
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(std::is_pod<GicCpuInterface>::value);
static_assert(sizeof(GicCpuInterface) == 0x2000);
struct KInterruptController {
NON_COPYABLE(KInterruptController);
NON_MOVEABLE(KInterruptController);
public:
static constexpr s32 NumSoftwareInterrupts = 16;
static constexpr s32 NumLocalInterrupts = NumSoftwareInterrupts + 16;
static constexpr s32 NumGlobalInterrupts = 988;
static constexpr s32 NumInterrupts = NumLocalInterrupts + NumGlobalInterrupts;
static constexpr s32 NumPriorityLevels = 4;
public:
struct LocalState {
u32 local_isenabler[NumLocalInterrupts / 32];
u32 local_ipriorityr[NumLocalInterrupts / 4];
u32 local_targetsr[NumLocalInterrupts / 4];
u32 local_icfgr[NumLocalInterrupts / 16];
};
struct GlobalState {
u32 global_isenabler[NumGlobalInterrupts / 32];
u32 global_ipriorityr[NumGlobalInterrupts / 4];
u32 global_targetsr[NumGlobalInterrupts / 4];
u32 global_icfgr[NumGlobalInterrupts / 16];
};
enum PriorityLevel : u8 {
PriorityLevel_High = 0,
PriorityLevel_Low = NumPriorityLevels - 1,
PriorityLevel_Timer = 1,
PriorityLevel_Scheduler = 2,
};
private:
static inline u32 s_mask[cpu::NumCores];
private:
volatile GicDistributor *gicd;
volatile GicCpuInterface *gicc;
public:
constexpr KInterruptController() : gicd(nullptr), gicc(nullptr) { /* ... */ }
void Initialize(s32 core_id);
void Finalize(s32 core_id);
public:
u32 GetIrq() const {
return this->gicc->iar;
}
static constexpr s32 ConvertRawIrq(u32 irq) {
return (irq == 0x3FF) ? -1 : (irq & 0x3FF);
}
void Enable(s32 irq) const {
this->gicd->isenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void Disable(s32 irq) const {
this->gicd->icenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void Clear(s32 irq) const {
this->gicd->icpendr[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void SetTarget(s32 irq, s32 core_id) const {
this->gicd->itargetsr.bytes[irq] |= GetGicMask(core_id);
}
void ClearTarget(s32 irq, s32 core_id) const {
this->gicd->itargetsr.bytes[irq] &= ~GetGicMask(core_id);
}
void SetPriorityLevel(s32 irq, s32 level) const {
MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low);
this->gicd->ipriorityr.bytes[irq] = ToGicPriorityValue(level);
}
s32 GetPriorityLevel(s32 irq) const {
return FromGicPriorityValue(this->gicd->ipriorityr.bytes[irq]);
}
void SetPriorityLevel(s32 level) const {
MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low);
this->gicc->pmr = ToGicPriorityValue(level);
}
void SetEdge(s32 irq) const {
u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)];
cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2))));
cfg |= (0x2 << (2 * (irq % (BITSIZEOF(u32) / 2))));
this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg;
}
void SetLevel(s32 irq) const {
u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)];
cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2))));
cfg |= (0x0 << (2 * (irq % (BITSIZEOF(u32) / 2))));
this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg;
}
void SendInterProcessorInterrupt(s32 irq, u64 core_mask) {
MESOSPHERE_ASSERT(IsSoftware(irq));
this->gicd->sgir = GetCpuTargetListMask(irq, core_mask);
}
void SendInterProcessorInterrupt(s32 irq) {
MESOSPHERE_ASSERT(IsSoftware(irq));
this->gicd->sgir = GicDistributor::SgirTargetListFilter_Others | irq;
}
void EndOfInterrupt(u32 irq) const {
this->gicc->eoir = irq;
}
bool IsInterruptDefined(s32 irq) {
const s32 num_interrupts = std::min(32 + 32 * (this->gicd->typer & 0x1F), static_cast<u32>(NumInterrupts));
return (0 <= irq && irq < num_interrupts);
}
/* TODO: Implement more KInterruptController functionality. */
public:
static constexpr ALWAYS_INLINE bool IsSoftware(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return id < NumSoftwareInterrupts;
}
static constexpr ALWAYS_INLINE bool IsLocal(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return id < NumLocalInterrupts;
}
static constexpr ALWAYS_INLINE bool IsGlobal(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return NumLocalInterrupts <= id;
}
static constexpr size_t GetGlobalInterruptIndex(s32 id) {
MESOSPHERE_ASSERT(IsGlobal(id));
return id - NumLocalInterrupts;
}
static constexpr size_t GetLocalInterruptIndex(s32 id) {
MESOSPHERE_ASSERT(IsLocal(id));
return id;
}
private:
static constexpr size_t PriorityShift = BITSIZEOF(u8) - __builtin_ctz(NumPriorityLevels);
static_assert(PriorityShift < BITSIZEOF(u8));
static constexpr ALWAYS_INLINE u8 ToGicPriorityValue(s32 level) {
return (level << PriorityShift) | ((1 << PriorityShift) - 1);
}
static constexpr ALWAYS_INLINE s32 FromGicPriorityValue(u8 priority) {
return (priority >> PriorityShift) & (NumPriorityLevels - 1);
}
static constexpr ALWAYS_INLINE s32 GetCpuTargetListMask(s32 irq, u64 core_mask) {
MESOSPHERE_ASSERT(IsSoftware(irq));
MESOSPHERE_ASSERT(core_mask < (1ul << cpu::NumCores));
return GicDistributor::SgirTargetListFilter_CpuTargetList | irq | (static_cast<u16>(core_mask) << GicDistributor::SgirCpuTargetListShift);
}
static ALWAYS_INLINE s32 GetGicMask(s32 core_id) {
return s_mask[core_id];
}
ALWAYS_INLINE void SetGicMask(s32 core_id) const {
s_mask[core_id] = this->gicd->itargetsr.bytes[0];
}
NOINLINE void SetupInterruptLines(s32 core_id) const;
};
}

View File

@@ -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/>.
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_interrupt_task.hpp>
#include <mesosphere/kern_select_interrupt_controller.hpp>
namespace ams::kern::arch::arm64 {
class KInterruptManager {
NON_COPYABLE(KInterruptManager);
NON_MOVEABLE(KInterruptManager);
private:
struct KCoreLocalInterruptEntry {
KInterruptHandler *handler;
bool manually_cleared;
bool needs_clear;
u8 priority;
constexpr KCoreLocalInterruptEntry()
: handler(nullptr), manually_cleared(false), needs_clear(false), priority(KInterruptController::PriorityLevel_Low)
{
/* ... */
}
};
struct KGlobalInterruptEntry {
KInterruptHandler *handler;
bool manually_cleared;
bool needs_clear;
constexpr KGlobalInterruptEntry() : handler(nullptr), manually_cleared(false), needs_clear(false) { /* ... */ }
};
private:
static KSpinLock s_lock;
static std::array<KGlobalInterruptEntry, KInterruptController::NumGlobalInterrupts> s_global_interrupts;
static KInterruptController::GlobalState s_global_state;
static bool s_global_state_saved;
private:
KCoreLocalInterruptEntry core_local_interrupts[KInterruptController::NumLocalInterrupts];
KInterruptController interrupt_controller;
KInterruptController::LocalState local_state;
bool local_state_saved;
private:
static ALWAYS_INLINE KSpinLock &GetLock() { return s_lock; }
static ALWAYS_INLINE KGlobalInterruptEntry &GetGlobalInterruptEntry(s32 irq) { return s_global_interrupts[KInterruptController::GetGlobalInterruptIndex(irq)]; }
ALWAYS_INLINE KCoreLocalInterruptEntry &GetLocalInterruptEntry(s32 irq) { return this->core_local_interrupts[KInterruptController::GetLocalInterruptIndex(irq)]; }
bool OnHandleInterrupt();
public:
constexpr KInterruptManager() : core_local_interrupts(), interrupt_controller(), local_state(), local_state_saved(false) { /* ... */ }
NOINLINE void Initialize(s32 core_id);
NOINLINE void Finalize(s32 core_id);
bool IsInterruptDefined(s32 irq) {
return this->interrupt_controller.IsInterruptDefined(irq);
}
NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level);
NOINLINE Result UnbindHandler(s32 irq, s32 core);
NOINLINE Result ClearInterrupt(s32 irq);
NOINLINE Result ClearInterrupt(s32 irq, s32 core_id);
ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) {
this->interrupt_controller.SendInterProcessorInterrupt(irq, core_mask);
}
ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq) {
this->interrupt_controller.SendInterProcessorInterrupt(irq);
}
static void HandleInterrupt(bool user_mode);
/* Implement more KInterruptManager functionality. */
private:
Result BindGlobal(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level);
Result BindLocal(KInterruptHandler *handler, s32 irq, s32 priority, bool manual_clear);
Result UnbindGlobal(s32 irq);
Result UnbindLocal(s32 irq);
Result ClearGlobal(s32 irq);
Result ClearLocal(s32 irq);
public:
static ALWAYS_INLINE u32 DisableInterrupts() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifset, #2"
: [intr_state]"=r"(intr_state)
:: "memory");
return intr_state;
}
static ALWAYS_INLINE u32 EnableInterrupts() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifclr, #2"
: [intr_state]"=r"(intr_state)
:: "memory");
return intr_state;
}
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
u64 cur_state;
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
}
static ALWAYS_INLINE bool AreInterruptsEnabled() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
return (intr_state & 0x80) == 0;
}
};
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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
namespace ams::kern::arch::arm64 {
namespace interrupt_name {
enum KInterruptName : s32 {
/* SGIs */
KInterruptName_ThreadTerminate = 4,
KInterruptName_CacheOperation = 5,
KInterruptName_Scheduler = 6,
KInterruptName_PerformanceCounter = 8,
/* PPIs */
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
KInterruptName_VirtualMaintenance = 25,
KInterruptName_HypervisorTimer = 26,
KInterruptName_VirtualTimer = 27,
KInterruptName_LegacyNFiq = 38,
KInterruptName_SecurePhysicalTimer = 29,
KInterruptName_NonSecurePhysicalTimer = 30,
KInterruptName_LegacyNIrq = 31,
#endif
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
KInterruptName_MemoryController = 109,
#endif
};
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_page_table_base.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
namespace ams::kern::arch::arm64 {
class KPageTable : public KPageTableBase {
NON_COPYABLE(KPageTable);
NON_MOVEABLE(KPageTable);
public:
using TraversalEntry = KPageTableImpl::TraversalEntry;
using TraversalContext = KPageTableImpl::TraversalContext;
enum BlockType {
BlockType_L3Block,
BlockType_L3ContiguousBlock,
BlockType_L2Block,
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
BlockType_L2TegraSmmuBlock,
#endif
BlockType_L2ContiguousBlock,
BlockType_L1Block,
BlockType_Count,
};
static_assert(L3BlockSize == PageSize);
static constexpr size_t ContiguousPageSize = L3ContiguousBlockSize;
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
static constexpr size_t L2TegraSmmuBlockSize = 2 * L2BlockSize;
#endif
static constexpr size_t BlockSizes[BlockType_Count] = {
[BlockType_L3Block] = L3BlockSize,
[BlockType_L3ContiguousBlock] = L3ContiguousBlockSize,
[BlockType_L2Block] = L2BlockSize,
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
[BlockType_L2TegraSmmuBlock] = L2TegraSmmuBlockSize,
#endif
[BlockType_L2ContiguousBlock] = L2ContiguousBlockSize,
[BlockType_L1Block] = L1BlockSize,
};
static constexpr BlockType GetMaxBlockType() {
return BlockType_L1Block;
}
static constexpr size_t GetBlockSize(BlockType type) {
return BlockSizes[type];
}
static constexpr BlockType GetBlockType(size_t size) {
switch (size) {
case L3BlockSize: return BlockType_L3Block;
case L3ContiguousBlockSize: return BlockType_L3ContiguousBlock;
case L2BlockSize: return BlockType_L2Block;
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
case L2TegraSmmuBlockSize: return BlockType_L2TegraSmmuBlock;
#endif
case L2ContiguousBlockSize: return BlockType_L2ContiguousBlock;
case L1BlockSize: return BlockType_L1Block;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
static constexpr size_t GetSmallerAlignment(size_t alignment) {
MESOSPHERE_ASSERT(alignment > L3BlockSize);
return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) - 1));
}
static constexpr size_t GetLargerAlignment(size_t alignment) {
MESOSPHERE_ASSERT(alignment < L1BlockSize);
return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) + 1));
}
private:
KPageTableManager *manager;
u64 ttbr;
u8 asid;
protected:
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup &page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
virtual void FinalizeUpdate(PageLinkedList *page_list) override;
KPageTableManager &GetPageTableManager() const { return *this->manager; }
private:
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
/* Set basic attributes. */
PageTableEntry entry;
entry.SetPrivilegedExecuteNever(true);
entry.SetAccessFlag(PageTableEntry::AccessFlag_Accessed);
entry.SetShareable(PageTableEntry::Shareable_InnerShareable);
if (!this->IsKernel()) {
entry.SetGlobal(false);
}
/* Set page attribute. */
if (properties.io) {
MESOSPHERE_ABORT_UNLESS(!properties.uncached);
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE)
.SetUserExecuteNever(true);
} else if (properties.uncached) {
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable);
} else {
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory);
}
/* Set user execute never bit. */
if (properties.perm != KMemoryPermission_UserReadExecute) {
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetUserExecuteNever(true);
}
/* Set can be contiguous. */
entry.SetContiguousAllowed(!properties.non_contiguous);
/* Set AP[1] based on perm. */
switch (properties.perm & KMemoryPermission_UserReadWrite) {
case KMemoryPermission_UserReadWrite:
case KMemoryPermission_UserRead:
entry.SetUserAccessible(true);
break;
case KMemoryPermission_KernelReadWrite:
case KMemoryPermission_KernelRead:
entry.SetUserAccessible(false);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Set AP[2] based on perm. */
switch (properties.perm & KMemoryPermission_UserReadWrite) {
case KMemoryPermission_UserReadWrite:
case KMemoryPermission_KernelReadWrite:
entry.SetReadOnly(false);
break;
case KMemoryPermission_KernelRead:
case KMemoryPermission_UserRead:
entry.SetReadOnly(true);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
return entry;
}
public:
constexpr KPageTable() : KPageTableBase(), manager(), ttbr(), asid() { /* ... */ }
static NOINLINE void Initialize(s32 core_id);
ALWAYS_INLINE void Activate(u32 proc_id) {
cpu::DataSynchronizationBarrier();
cpu::SwitchProcess(this->ttbr, proc_id);
}
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager);
Result Finalize();
private:
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
Result Unmap(KProcessAddress virt_addr, size_t num_pages, PageLinkedList *page_list, bool force, bool reuse_ll);
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, size_t page_size, PageLinkedList *page_list, bool reuse_ll) {
switch (page_size) {
case L1BlockSize:
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
case L2TegraSmmuBlockSize:
#endif
case L2BlockSize:
case L3BlockSize:
break;
case L2ContiguousBlockSize:
case L3ContiguousBlockSize:
entry_template.SetContiguous(true);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
return this->Map(virt_addr, phys_addr, num_pages, entry_template, page_list, reuse_ll);
}
Result MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
Result MapGroup(KProcessAddress virt_addr, const KPageGroup &pg, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
bool MergePages(KProcessAddress virt_addr, PageLinkedList *page_list);
ALWAYS_INLINE Result SeparatePagesImpl(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
Result SeparatePages(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
static void PteDataSynchronizationBarrier() {
cpu::DataSynchronizationBarrierInnerShareable();
}
static void ClearPageTable(KVirtualAddress table) {
cpu::ClearPageToZero(GetVoidPointer(table));
}
void OnTableUpdated() const {
cpu::InvalidateTlbByAsid(this->asid);
}
void OnKernelTableUpdated() const {
cpu::InvalidateEntireTlbDataOnly();
}
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
cpu::InvalidateTlbByVaDataOnly(virt_addr);
}
void NoteUpdated() const {
cpu::DataSynchronizationBarrier();
if (this->IsKernel()) {
this->OnKernelTableUpdated();
} else {
this->OnTableUpdated();
}
}
void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
MESOSPHERE_ASSERT(this->IsKernel());
cpu::DataSynchronizationBarrier();
this->OnKernelTableSinglePageUpdated(virt_addr);
}
KVirtualAddress AllocatePageTable(PageLinkedList *page_list, bool reuse_ll) const {
KVirtualAddress table = this->GetPageTableManager().Allocate();
if (table == Null<KVirtualAddress>) {
if (reuse_ll && page_list->Peek()) {
table = KVirtualAddress(reinterpret_cast<uintptr_t>(page_list->Pop()));
} else {
return Null<KVirtualAddress>;
}
}
ClearPageTable(table);
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
return table;
}
void FreePageTable(PageLinkedList *page_list, KVirtualAddress table) const {
MESOSPHERE_ASSERT(this->GetPageTableManager().IsInPageTableHeap(table));
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
page_list->Push(table);
}
};
}

View File

@@ -0,0 +1,288 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern::arch::arm64 {
constexpr size_t L1BlockSize = 1_GB;
constexpr size_t L1ContiguousBlockSize = 0x10 * L1BlockSize;
constexpr size_t L2BlockSize = 2_MB;
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
constexpr size_t L3BlockSize = PageSize;
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
class PageTableEntry {
public:
struct InvalidTag{};
enum Permission : u64 {
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
};
enum Shareable : u64 {
Shareable_NonShareable = (0 << 8),
Shareable_OuterShareable = (2 << 8),
Shareable_InnerShareable = (3 << 8),
};
/* Official attributes are: */
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
enum PageAttribute : u64 {
PageAttribute_Device_nGnRnE = (0 << 2),
PageAttribute_Device_nGnRE = (1 << 2),
PageAttribute_NormalMemory = (2 << 2),
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
};
enum AccessFlag : u64 {
AccessFlag_NotAccessed = (0 << 10),
AccessFlag_Accessed = (1 << 10),
};
enum Type : u64 {
Type_None = 0x0,
Type_L1Block = 0x1,
Type_L1Table = 0x3,
Type_L2Block = 0x1,
Type_L2Table = 0x3,
Type_L3Block = 0x3,
};
enum ContigType : u64 {
ContigType_NotContiguous = (0x0ul << 52),
ContigType_Contiguous = (0x1ul << 52),
};
protected:
u64 attributes;
public:
/* Take in a raw attribute. */
constexpr ALWAYS_INLINE PageTableEntry() : attributes() { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(InvalidTag) : attributes(0) { /* ... */ }
/* Extend a previous attribute. */
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
/* Construct a new attribute. */
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share))
{
/* ... */
}
protected:
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->attributes >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
return this->attributes & (((1ul << count) - 1) << offset);
}
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->attributes &= ~mask;
this->attributes |= (value & (mask >> offset)) << offset;
}
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->attributes &= ~mask;
this->attributes |= (value & mask);
}
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
const u64 mask = 1ul << offset;
if (enabled) {
this->attributes |= mask;
} else {
this->attributes &= ~mask;
}
}
public:
constexpr ALWAYS_INLINE bool IsContiguousAllowed() const { return this->GetBits(55, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
constexpr ALWAYS_INLINE bool IsGlobal() const { return this->GetBits(11, 1) == 0; }
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
constexpr ALWAYS_INLINE bool IsReadOnly() const { return this->GetBits(7, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserAccessible() const { return this->GetBits(6, 1) != 0; }
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; }
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE decltype(auto) SetContiguousAllowed(bool en) { this->SetBit(55, !en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserExecuteNever(bool en) { this->SetBit(54, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetPrivilegedExecuteNever(bool en) { this->SetBit(53, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetContiguous(bool en) { this->SetBit(52, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetGlobal(bool en) { this->SetBit(11, !en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetAccessFlag(AccessFlag f) { this->SetBitsDirect(10, 1, f); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetShareable(Shareable s) { this->SetBitsDirect(8, 2, s); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetReadOnly(bool en) { this->SetBit(7, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserAccessible(bool en) { this->SetBit(6, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetPageAttribute(PageAttribute a) { this->SetBitsDirect(2, 3, a); return *this; }
constexpr ALWAYS_INLINE u64 GetEntryTemplate() const {
constexpr u64 Mask = (0xFFF0000000000FFFul & ~u64(0x3ul | (0x1ul << 52)));
return this->attributes & Mask;
}
constexpr ALWAYS_INLINE bool Is(u64 attr) const {
return this->attributes == attr;
}
protected:
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
return this->attributes;
}
};
static_assert(sizeof(PageTableEntry) == sizeof(u64));
constexpr inline PageTableEntry InvalidPageTableEntry = PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
class L1PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L1PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(30, 18);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
if (this->IsTable()) {
out = this->GetTable();
return true;
} else {
return false;
}
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L2PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L2PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(21, 27);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
if (this->IsTable()) {
out = this->GetTable();
return true;
} else {
return false;
}
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L3PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L3PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
constexpr inline L1PageTableEntry InvalidL1PageTableEntry = L1PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline L2PageTableEntry InvalidL2PageTableEntry = L2PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline L3PageTableEntry InvalidL3PageTableEntry = L3PageTableEntry(PageTableEntry::InvalidTag{});
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
namespace ams::kern::arch::arm64 {
class KPageTableImpl {
NON_COPYABLE(KPageTableImpl);
NON_MOVEABLE(KPageTableImpl);
public:
struct TraversalEntry {
KPhysicalAddress phys_addr;
size_t block_size;
};
struct TraversalContext {
const L1PageTableEntry *l1_entry;
const L2PageTableEntry *l2_entry;
const L3PageTableEntry *l3_entry;
};
private:
static constexpr size_t PageBits = __builtin_ctzll(PageSize);
static constexpr size_t NumLevels = 3;
static constexpr size_t LevelBits = 9;
static_assert(NumLevels > 0);
template<size_t Offset, size_t Count>
static constexpr ALWAYS_INLINE u64 GetBits(u64 value) {
return (value >> Offset) & ((1ul << Count) - 1);
}
template<size_t Offset, size_t Count>
constexpr ALWAYS_INLINE u64 SelectBits(u64 value) {
return value & (((1ul << Count) - 1) << Offset);
}
static constexpr ALWAYS_INLINE uintptr_t GetL0Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 0), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL1Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 1), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL2Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 2), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL3Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 3), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL1Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 1)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL2Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 2)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL3Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 3)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL1Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 1) + 4>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL2Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 2) + 4>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL3Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 3) + 4>(GetInteger(addr)); }
static ALWAYS_INLINE KVirtualAddress GetPageTableVirtualAddress(KPhysicalAddress addr) {
return KMemoryLayout::GetLinearVirtualAddress(addr);
}
ALWAYS_INLINE bool ExtractL1Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L1PageTableEntry *l1_entry, KProcessAddress virt_addr) const;
ALWAYS_INLINE bool ExtractL2Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L2PageTableEntry *l2_entry, KProcessAddress virt_addr) const;
ALWAYS_INLINE bool ExtractL3Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L3PageTableEntry *l3_entry, KProcessAddress virt_addr) const;
private:
L1PageTableEntry *table;
bool is_kernel;
u32 num_entries;
public:
ALWAYS_INLINE KVirtualAddress GetTableEntry(KVirtualAddress table, size_t index) const {
return table + index * sizeof(PageTableEntry);
}
ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KProcessAddress address) const {
return GetPointer<L1PageTableEntry>(GetTableEntry(KVirtualAddress(this->table), GetL1Index(address) & (this->num_entries - 1)));
}
ALWAYS_INLINE L2PageTableEntry *GetL2EntryFromTable(KVirtualAddress table, KProcessAddress address) const {
return GetPointer<L2PageTableEntry>(GetTableEntry(table, GetL2Index(address)));
}
ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KProcessAddress address) const {
return GetL2EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
}
ALWAYS_INLINE L3PageTableEntry *GetL3EntryFromTable(KVirtualAddress table, KProcessAddress address) const {
return GetPointer<L3PageTableEntry>(GetTableEntry(table, GetL3Index(address)));
}
ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KProcessAddress address) const {
return GetL3EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
}
public:
constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ }
NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end);
NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end);
L1PageTableEntry *Finalize();
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;
bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const;
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const;
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table.hpp>
namespace ams::kern::arch::arm64 {
class KProcessPageTable {
private:
KPageTable page_table;
public:
constexpr KProcessPageTable() : page_table() { /* ... */ }
void Activate(u64 id) {
/* Activate the page table with the specified contextidr. */
this->page_table.Activate(id);
}
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
return this->page_table.InitializeForProcess(id, as_type, enable_aslr, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager);
}
void Finalize() { this->page_table.Finalize(); }
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) {
return this->page_table.SetMemoryPermission(addr, size, perm);
}
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) {
return this->page_table.SetProcessMemoryPermission(addr, size, perm);
}
Result SetHeapSize(KProcessAddress *out, size_t size) {
return this->page_table.SetHeapSize(out, size);
}
Result SetMaxHeapSize(size_t size) {
return this->page_table.SetMaxHeapSize(size);
}
Result QueryInfo(KMemoryInfo *out_info, ams::svc::PageInfo *out_page_info, KProcessAddress addr) const {
return this->page_table.QueryInfo(out_info, out_page_info, addr);
}
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
return this->page_table.MapIo(phys_addr, size, perm);
}
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
return this->page_table.MapStatic(phys_addr, size, perm);
}
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
return this->page_table.MapRegion(region_type, perm);
}
Result MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPageGroup(addr, pg, state, perm);
}
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm);
}
Result MapPages(KProcessAddress *out_addr, size_t num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, state, perm);
}
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
return this->page_table.UnmapPages(addr, num_pages, state);
}
Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
}
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
return this->page_table.GetPhysicalAddress(out, address);
}
bool Contains(KProcessAddress addr, size_t size) const { return this->page_table.Contains(addr, size); }
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return this->page_table.CanContain(addr, size, state); }
KProcessAddress GetAddressSpaceStart() const { return this->page_table.GetAddressSpaceStart(); }
KProcessAddress GetHeapRegionStart() const { return this->page_table.GetHeapRegionStart(); }
KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); }
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); }
KProcessAddress GetAliasCodeRegionStart() const { return this->page_table.GetAliasCodeRegionStart(); }
size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); }
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
size_t GetAliasCodeRegionSize() const { return this->page_table.GetAliasCodeRegionSize(); }
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const {
/* TODO: Better way to convert address type? */
return this->page_table.GetHeapPhysicalAddress(address);
}
KBlockInfoManager *GetBlockInfoManager() {
return this->page_table.GetBlockInfoManager();
}
};
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_select_cpu.hpp>
namespace ams::kern::arch::arm64 {
class KNotAlignedSpinLock {
private:
u32 packed_tickets;
public:
constexpr KNotAlignedSpinLock() : packed_tickets(0) { /* ... */ }
void Lock() {
u32 tmp0, tmp1;
__asm__ __volatile__(
" prfm pstl1keep, %[packed_tickets]\n"
"1:\n"
" ldaxr %w[tmp0], %[packed_tickets]\n"
" add %w[tmp0], %w[tmp0], #0x10000\n"
" stxr %w[tmp1], %w[tmp0], %[packed_tickets]\n"
" cbnz %w[tmp1], 1b\n"
" \n"
" and %w[tmp1], %w[tmp0], #0xFFFF\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.eq done"
" sevl\n"
"2:\n"
" wfe\n"
" ldaxrh %w[tmp1], %[packed_tickets]\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.ne 2b\n"
"done:\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [packed_tickets]"+Q"(this->packed_tickets)
:
: "cc", "memory"
);
}
void Unlock() {
const u32 value = this->packed_tickets + 1;
__asm__ __volatile__(
" stlrh %w[value], %[packed_tickets]\n"
: [packed_tickets]"+Q"(this->packed_tickets)
: [value]"r"(value)
: "memory"
);
}
};
static_assert(sizeof(KNotAlignedSpinLock) == sizeof(u32));
class KAlignedSpinLock {
private:
alignas(cpu::DataCacheLineSize) u16 current_ticket;
alignas(cpu::DataCacheLineSize) u16 next_ticket;
public:
constexpr KAlignedSpinLock() : current_ticket(0), next_ticket(0) { /* ... */ }
void Lock() {
u32 tmp0, tmp1, got_lock;
__asm__ __volatile__(
" prfm pstl1keep, %[next_ticket]\n"
"1:\n"
" ldaxrh %w[tmp0], %[next_ticket]\n"
" add %w[tmp1], %w[tmp0], #0x1\n"
" stxrh %w[got_lock], %w[tmp1], %[next_ticket]\n"
" cbnz %w[got_lock], 1b\n"
" \n"
" sevl\n"
"2:\n"
" wfe\n"
" ldaxrh %w[tmp1], %[current_ticket]\n"
" cmp %w[tmp1], %w[tmp0]\n"
" b.ne 2b\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [got_lock]"=&r"(got_lock), [next_ticket]"+Q"(this->next_ticket)
: [current_ticket]"Q"(this->current_ticket)
: "cc", "memory"
);
}
void Unlock() {
const u32 value = this->current_ticket + 1;
__asm__ __volatile__(
" stlrh %w[value], %[current_ticket]\n"
: [current_ticket]"+Q"(this->current_ticket)
: [value]"r"(value)
: "memory"
);
}
};
static_assert(sizeof(KAlignedSpinLock) == 2 * cpu::DataCacheLineSize);
using KSpinLock = KAlignedSpinLock;
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table.hpp>
namespace ams::kern::arch::arm64 {
class KSupervisorPageTable {
private:
KPageTable page_table;
u64 ttbr0[cpu::NumCores];
public:
constexpr KSupervisorPageTable() : page_table(), ttbr0() { /* ... */ }
NOINLINE void Initialize(s32 core_id);
void Activate() {
/* Activate, using process id = 0xFFFFFFFF */
this->page_table.Activate(0xFFFFFFFF);
}
void ActivateForInit() {
this->Activate();
/* Invalidate entire TLB. */
cpu::InvalidateEntireTlb();
}
void Finalize(s32 core_id);
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, region_start, region_num_pages, state, perm);
}
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
return this->page_table.UnmapPages(address, num_pages, state);
}
Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPageGroup(out_addr, pg, region_start, region_num_pages, state, perm);
}
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) {
return this->page_table.UnmapPageGroup(address, pg, state);
}
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
return this->page_table.GetPhysicalAddress(out, address);
}
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern {
class KThread;
}
namespace ams::kern::arch::arm64 {
class KThreadContext {
public:
static constexpr size_t NumCalleeSavedRegisters = (29 - 19) + 1;
static constexpr size_t NumFpuRegisters = 32;
private:
union {
u64 registers[NumCalleeSavedRegisters];
struct {
u64 x19;
u64 x20;
u64 x21;
u64 x22;
u64 x23;
u64 x24;
u64 x25;
u64 x26;
u64 x27;
u64 x28;
u64 x29;
};
} callee_saved;
u64 lr;
u64 sp;
u64 cpacr;
u64 fpcr;
u64 fpsr;
alignas(0x10) u128 fpu_registers[NumFpuRegisters];
bool locked;
private:
static void RestoreFpuRegisters64(const KThreadContext &);
static void RestoreFpuRegisters32(const KThreadContext &);
public:
constexpr explicit KThreadContext() : callee_saved(), lr(), sp(), cpacr(), fpcr(), fpsr(), fpu_registers(), locked() { /* ... */ }
Result Initialize(KVirtualAddress u_pc, KVirtualAddress k_sp, KVirtualAddress u_sp, uintptr_t arg, bool is_user, bool is_64_bit, bool is_main);
Result Finalize();
void SetArguments(uintptr_t arg0, uintptr_t arg1);
static void FpuContextSwitchHandler(KThread *thread);
/* TODO: More methods (especially FPU management) */
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::arch::arm64 {
void UserspaceAccessFunctionAreaBegin();
class UserspaceAccess {
public:
static bool CopyMemoryFromUser(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned32Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned64Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserSize32Bit(void *dst, const void *src);
static s32 CopyStringFromUser(void *dst, const void *src, size_t size);
static bool CopyMemoryToUser(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserAligned32Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserAligned64Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserSize32Bit(void *dst, const void *src);
static s32 CopyStringToUser(void *dst, const void *src, size_t size);
static bool ClearMemory(void *dst, size_t size);
static bool ClearMemoryAligned32Bit(void *dst, size_t size);
static bool ClearMemoryAligned64Bit(void *dst, size_t size);
static bool ClearMemorySize32Bit(void *dst);
static bool StoreDataCache(uintptr_t start, uintptr_t end);
static bool FlushDataCache(uintptr_t start, uintptr_t end);
static bool InvalidateDataCache(uintptr_t start, uintptr_t end);
static bool InvalidateInstructionCache(uintptr_t start, uintptr_t end);
static bool ReadIoMemory32Bit(void *dst, const void *src, size_t size);
static bool ReadIoMemory16Bit(void *dst, const void *src, size_t size);
static bool ReadIoMemory8Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory32Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory16Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory8Bit(void *dst, const void *src, size_t size);
};
void UserspaceAccessFunctionAreaEnd();
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_memory_manager.hpp>
#include <mesosphere/kern_select_page_table.hpp>
namespace ams::kern::board::nintendo::nx {
using KDeviceVirtualAddress = u64;
class KDevicePageTable {
private:
static constexpr size_t TableCount = 4;
private:
KVirtualAddress tables[TableCount];
u8 table_asids[TableCount];
u64 attached_device;
u32 attached_value;
u32 detached_value;
u32 hs_attached_value;
u32 hs_detached_value;
private:
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
return KPageTable::GetHeapVirtualAddress(addr);
}
static ALWAYS_INLINE KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
return KPageTable::GetHeapPhysicalAddress(addr);
}
static ALWAYS_INLINE KVirtualAddress GetPageTableVirtualAddress(KPhysicalAddress addr) {
return KPageTable::GetPageTableVirtualAddress(addr);
}
static ALWAYS_INLINE KPhysicalAddress GetPageTablePhysicalAddress(KVirtualAddress addr) {
return KPageTable::GetPageTablePhysicalAddress(addr);
}
public:
constexpr KDevicePageTable() : tables(), table_asids(), attached_device(), attached_value(), detached_value(), hs_attached_value(), hs_detached_value() { /* ... */ }
static void Initialize();
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::board::nintendo::nx {
class KSystemControl {
public:
class Init {
public:
/* Initialization. */
static size_t GetIntendedMemorySize();
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static bool ShouldIncreaseThreadResourceLimit();
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
static size_t GetApplicationPoolSize();
static size_t GetAppletPoolSize();
static size_t GetMinimumNonSecureSystemPoolSize();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
};
public:
/* Initialization. */
static NOINLINE void InitializePhase1();
static NOINLINE void InitializePhase2();
static NOINLINE u32 GetInitialProcessBinaryPool();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
/* Privileged Access. */
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static void ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
return v;
}
static ALWAYS_INLINE void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
}
/* Power management. */
static void SleepSystem();
static NORETURN void StopSystem();
/* User access. */
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
};
}

View File

@@ -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 <mesosphere/kern_k_typed_address.hpp>
#ifdef ATMOSPHERE_ARCH_ARM64
#include <mesosphere/arch/arm64/init/kern_k_init_arguments.hpp>
#else
#error "Unknown architecture for KInitArguments"
#endif
namespace ams::kern::init {
KPhysicalAddress GetInitArgumentsAddress(s32 core_id);
void SetInitArguments(s32 core_id, KPhysicalAddress address, uintptr_t arg);
void StoreInitArguments();
}

View File

@@ -17,23 +17,21 @@
#include <vapours.hpp>
#ifdef ATMOSPHERE_ARCH_ARM64
#include "kern_init_elf64.hpp"
#include <mesosphere/init/kern_init_elf64.hpp>
namespace ams::kern::init::Elf {
using namespace ams::kern::init::Elf::Elf64;
enum RelocationType {
R_ARCHITECTURE_RELATIVE = 0x403, /* Real name R_AARCH64_RELATIVE */
};
}
#else
#error "Unknown Architecture"
#endif
namespace ams::kern::init::Elf {
#ifdef ATMOSPHERE_ARCH_ARM64
using namespace ams::kern::init::Elf::Elf64;
enum RelocationType {
R_ARCHITECTURE_RELATIVE = 0x403, /* Real name R_AARCH64_RELATIVE */
};
#else
#error "Unknown Architecture"
#endif
/* API to apply relocations or call init array. */
void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic);
void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end);

View File

@@ -27,7 +27,7 @@ namespace ams::kern::init {
u32 rw_end_offset;
u32 bss_offset;
u32 bss_end_offset;
u32 ini_end_offset;
u32 ini_load_offset;
u32 dynamic_offset;
u32 init_array_offset;
u32 init_array_end_offset;

View File

@@ -14,9 +14,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#ifdef ATMOSPHERE_ARCH_ARM64
#include "../arch/arm64/init/kern_k_init_page_table.hpp"
#include <mesosphere/arch/arm64/init/kern_k_init_page_table.hpp>
namespace ams::kern::init {
using ams::kern::arch::arm64::PageTableEntry;
using ams::kern::arch::arm64::init::KInitialPageTable;
using ams::kern::arch::arm64::init::KInitialPageAllocator;
}
#else
#error "Unknown architecture for KInitialPageTable"
#endif

View File

@@ -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>
#include <mesosphere/kern_k_slab_heap.hpp>
namespace ams::kern::init {
struct KSlabResourceCounts {
size_t num_KProcess;
size_t num_KThread;
size_t num_KEvent;
size_t num_KInterruptEvent;
size_t num_KPort;
size_t num_KSharedMemory;
size_t num_KTransferMemory;
size_t num_KCodeMemory;
size_t num_KDeviceAddressSpace;
size_t num_KSession;
size_t num_KLightSession;
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
};
NOINLINE void InitializeSlabResourceCounts();
const KSlabResourceCounts &GetSlabResourceCounts();
size_t CalculateTotalSlabHeapSize();
NOINLINE void InitializeKPageBufferSlabHeap();
NOINLINE void InitializeSlabHeaps();
}

View File

@@ -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>
namespace ams::kern {
constexpr size_t PageSize = 4_KB;
}
#if 1 || defined(AMS_BUILD_FOR_AUDITING)
#define MESOSPHERE_BUILD_FOR_AUDITING
#endif
#if defined(MESOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
#define MESOSPHERE_BUILD_FOR_DEBUGGING
#endif
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
#define MESOSPHERE_ENABLE_ASSERTIONS
#define MESOSPHERE_ENABLE_DEBUG_PRINT
#endif
#include <mesosphere/svc/kern_svc_results.hpp>

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
namespace ams::kern {
class KDebugLog {
private:
static NOINLINE void VSNPrintf(char *dst, const size_t dst_size, const char *format, ::std::va_list vl);
public:
static NOINLINE void Initialize();
static NOINLINE void Printf(const char *format, ...) __attribute__((format(printf, 1, 2)));
static NOINLINE void VPrintf(const char *format, ::std::va_list vl);
};
}
#ifndef MESOSPHERE_DEBUG_LOG_SELECTED
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
#define MESOSPHERE_DEBUG_LOG_USE_UART_C
#else
#error "Unknown board for Default Debug Log Source"
#endif
#define MESOSPHERE_DEBUG_LOG_SELECTED
#endif
#define MESOSPHERE_RELEASE_LOG(fmt, ...) ::ams::kern::KDebugLog::Printf((fmt), ## __VA_ARGS__)
#define MESOSPHERE_RELEASE_VLOG(fmt, vl) ::ams::kern::KDebugLog::VPrintf((fmt), (vl))
#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT
#define MESOSPHERE_LOG(fmt, ...) MESOSPHERE_RELEASE_LOG((fmt), ## __VA_ARGS__)
#define MESOSPHERE_VLOG(fmt, vl) MESOSPHERE_RELEASE_VLOG((fmt), (vl))
#else
#define MESOSPHERE_LOG(fmt, ...) do { MESOSPHERE_UNUSED(fmt); MESOSPHERE_UNUSED(__VA_ARGS__); } while (0)
#define MESOSPHERE_VLOG(fmt, vl) do { MESOSPHERE_UNUSED(fmt); MESOSPHERE_UNUSED(vl); } while (0)
#endif

View File

@@ -14,8 +14,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include "kern_panic.hpp"
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_initial_process_reader.hpp>
namespace ams::kern {
@@ -29,4 +29,10 @@ namespace ams::kern {
u32 reserved;
};
NOINLINE void CopyInitialProcessBinaryToKernelMemory();
NOINLINE void CreateAndRunInitialProcesses();
u64 GetInitialProcessIdMin();
u64 GetInitialProcessIdMax();
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_condition_variable.hpp>
namespace ams::kern {
class KAddressArbiter {
public:
using ThreadTree = KConditionVariable::ThreadTree;
private:
ThreadTree tree;
public:
constexpr KAddressArbiter() : tree() { /* ... */ }
Result SignalToAddress(uintptr_t addr, ams::svc::SignalType type, s32 value, s32 count) {
switch (type) {
case ams::svc::SignalType_Signal:
return this->Signal(addr, count);
case ams::svc::SignalType_SignalAndIncrementIfEqual:
return this->SignalAndIncrementIfEqual(addr, value, count);
case ams::svc::SignalType_SignalAndModifyByWaitingCountIfEqual:
return this->SignalAndModifyByWaitingCountIfEqual(addr, value, count);
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
Result WaitForAddress(uintptr_t addr, ams::svc::ArbitrationType type, s32 value, s64 timeout) {
switch (type) {
case ams::svc::ArbitrationType_WaitIfLessThan:
return this->WaitIfLessThan(addr, value, false, timeout);
case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan:
return this->WaitIfLessThan(addr, value, true, timeout);
case ams::svc::ArbitrationType_WaitIfEqual:
return this->WaitIfEqual(addr, value, timeout);
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
private:
Result Signal(uintptr_t addr, s32 count);
Result SignalAndIncrementIfEqual(uintptr_t addr, s32 value, s32 count);
Result SignalAndModifyByWaitingCountIfEqual(uintptr_t addr, s32 value, s32 count);
Result WaitIfLessThan(uintptr_t addr, s32 value, bool decrement, s64 timeout);
Result WaitIfEqual(uintptr_t addr, s32 value, s64 timeout);
};
}

View File

@@ -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
namespace ams::kern {
struct KAddressSpaceInfo {
public:
enum Type {
Type_32Bit = 0,
Type_Small64Bit = 1,
Type_Large64Bit = 2,
Type_Heap = 3,
Type_Stack = 4,
Type_Alias = 5,
Type_Count,
};
private:
size_t bit_width;
size_t address;
size_t size;
Type type;
public:
static uintptr_t GetAddressSpaceStart(size_t width, Type type);
static size_t GetAddressSpaceSize(size_t width, Type type);
constexpr KAddressSpaceInfo(size_t bw, size_t a, size_t s, Type t) : bit_width(bw), address(a), size(s), type(t) { /* ... */ }
constexpr size_t GetWidth() const { return this->bit_width; }
constexpr size_t GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
constexpr Type GetType() const { return this->type; }
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
namespace ams::kern {
class KAffinityMask {
private:
static constexpr u64 AllowedAffinityMask = (1ul << cpu::NumCores) - 1;
private:
u64 mask;
private:
static constexpr ALWAYS_INLINE u64 GetCoreBit(s32 core) {
MESOSPHERE_ASSERT(0 <= core && core < static_cast<s32>(cpu::NumCores));
return (1ul << core);
}
public:
constexpr ALWAYS_INLINE KAffinityMask() : mask(0) { MESOSPHERE_ASSERT_THIS(); }
constexpr ALWAYS_INLINE u64 GetAffinityMask() const { return this->mask; }
constexpr ALWAYS_INLINE void SetAffinityMask(u64 new_mask) {
MESOSPHERE_ASSERT((new_mask & ~AllowedAffinityMask) == 0);
this->mask = new_mask;
}
constexpr ALWAYS_INLINE bool GetAffinity(s32 core) const {
return this->mask & GetCoreBit(core);
}
constexpr ALWAYS_INLINE void SetAffinity(s32 core, bool set) {
MESOSPHERE_ASSERT(0 <= core && core < static_cast<s32>(cpu::NumCores));
if (set) {
this->mask |= GetCoreBit(core);
} else {
this->mask &= ~GetCoreBit(core);
}
}
constexpr ALWAYS_INLINE void SetAll() {
this->mask = AllowedAffinityMask;
}
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_k_class_token.hpp>
namespace ams::kern {
class KProcess;
#define MESOSPHERE_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
NON_COPYABLE(CLASS); \
NON_MOVEABLE(CLASS); \
private: \
friend class ::ams::kern::KClassTokenGenerator; \
static constexpr inline auto ObjectType = ::ams::kern::KClassTokenGenerator::ObjectType::CLASS; \
static constexpr inline const char * const TypeName = #CLASS; \
static constexpr inline ClassTokenType ClassToken() { return ::ams::kern::ClassToken<CLASS>; } \
public: \
using BaseClass = BASE_CLASS; \
static constexpr ALWAYS_INLINE TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
return TypeObj(TypeName, Token); \
} \
static constexpr ALWAYS_INLINE const char *GetStaticTypeName() { return TypeName; } \
virtual TypeObj GetTypeObj() const { return GetStaticTypeObj(); } \
virtual const char *GetTypeName() { return GetStaticTypeName(); } \
private:
class KAutoObject {
protected:
class TypeObj {
private:
const char *name;
ClassTokenType class_token;
public:
constexpr explicit TypeObj(const char *n, ClassTokenType tok) : name(n), class_token(tok) { /* ... */ }
constexpr ALWAYS_INLINE const char *GetName() const { return this->name; }
constexpr ALWAYS_INLINE ClassTokenType GetClassToken() const { return this->class_token; }
constexpr ALWAYS_INLINE bool operator==(const TypeObj &rhs) {
return this->GetClassToken() == rhs.GetClassToken();
}
constexpr ALWAYS_INLINE bool operator!=(const TypeObj &rhs) {
return this->GetClassToken() != rhs.GetClassToken();
}
constexpr ALWAYS_INLINE bool IsDerivedFrom(const TypeObj &rhs) {
return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
}
};
private:
MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
private:
std::atomic<u32> ref_count;
public:
static KAutoObject *Create(KAutoObject *ptr);
public:
constexpr ALWAYS_INLINE explicit KAutoObject() : ref_count(0) { MESOSPHERE_ASSERT_THIS(); }
virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); }
/* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */
virtual void Destroy() { MESOSPHERE_ASSERT_THIS(); }
/* Finalize is responsible for cleaning up resource, but does not destroy the object. */
virtual void Finalize() { MESOSPHERE_ASSERT_THIS(); }
virtual KProcess *GetOwner() const { return nullptr; }
u32 GetReferenceCount() const {
return this->ref_count;
}
ALWAYS_INLINE bool IsDerivedFrom(const TypeObj &rhs) const {
return this->GetTypeObj().IsDerivedFrom(rhs);
}
ALWAYS_INLINE bool IsDerivedFrom(const KAutoObject &rhs) const {
return this->IsDerivedFrom(rhs.GetTypeObj());
}
template<typename Derived>
ALWAYS_INLINE Derived DynamicCast() {
static_assert(std::is_pointer<Derived>::value);
using DerivedType = typename std::remove_pointer<Derived>::type;
if (AMS_LIKELY(this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
template<typename Derived>
ALWAYS_INLINE const Derived DynamicCast() const {
static_assert(std::is_pointer<Derived>::value);
using DerivedType = typename std::remove_pointer<Derived>::type;
if (AMS_LIKELY(this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
ALWAYS_INLINE bool Open() {
MESOSPHERE_ASSERT_THIS();
/* Atomically increment the reference count, only if it's positive. */
u32 cur_ref_count = this->ref_count.load(std::memory_order_acquire);
do {
if (AMS_UNLIKELY(cur_ref_count == 0)) {
return false;
}
MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1);
} while (!this->ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed));
return true;
}
ALWAYS_INLINE void Close() {
MESOSPHERE_ASSERT_THIS();
/* Atomically decrement the reference count, not allowing it to become negative. */
u32 cur_ref_count = this->ref_count.load(std::memory_order_acquire);
do {
MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0);
} while (!this->ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed));
/* If ref count hits zero, destroy the object. */
if (cur_ref_count - 1 == 0) {
this->Destroy();
}
}
};
class KAutoObjectWithListContainer;
class KAutoObjectWithList : public KAutoObject {
private:
friend class KAutoObjectWithListContainer;
private:
util::IntrusiveRedBlackTreeNode list_node;
public:
static ALWAYS_INLINE int Compare(const KAutoObjectWithList &lhs, const KAutoObjectWithList &rhs) {
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
if (lid < rid) {
return -1;
} else if (lid > rid) {
return 1;
} else {
return 0;
}
}
public:
virtual u64 GetId() const {
return reinterpret_cast<u64>(this);
}
};
template<typename T>
class KScopedAutoObject {
static_assert(std::is_base_of<KAutoObject, T>::value);
NON_COPYABLE(KScopedAutoObject);
private:
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;
}
public:
constexpr ALWAYS_INLINE KScopedAutoObject() : obj(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE KScopedAutoObject(T *o) : obj(o) {
if (this->obj != nullptr) {
this->obj->Open();
}
}
ALWAYS_INLINE ~KScopedAutoObject() {
if (this->obj != nullptr) {
this->obj->Close();
}
this->obj = nullptr;
}
constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) {
this->obj = rhs.obj;
rhs.obj = nullptr;
}
constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) {
rhs.Swap(*this);
return *this;
}
constexpr ALWAYS_INLINE T *operator->() { return this->obj; }
constexpr ALWAYS_INLINE T &operator*() { return *this->obj; }
constexpr ALWAYS_INLINE void Reset(T *o) {
KScopedAutoObject(o).Swap(*this);
}
constexpr ALWAYS_INLINE T *GetPointerUnsafe() { return this->obj; }
constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; }
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
namespace ams::kern {
class KAutoObjectWithListContainer {
NON_COPYABLE(KAutoObjectWithListContainer);
NON_MOVEABLE(KAutoObjectWithListContainer);
private:
using ListType = util::IntrusiveRedBlackTreeMemberTraits<&KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
public:
class ListAccessor : public KScopedLightLock {
private:
ListType &list;
public:
explicit ListAccessor(KAutoObjectWithListContainer *container) : KScopedLightLock(container->lock), list(container->object_list) { /* ... */ }
explicit ListAccessor(KAutoObjectWithListContainer &container) : KScopedLightLock(container.lock), list(container.object_list) { /* ... */ }
typename ListType::iterator begin() const {
return this->list.begin();
}
typename ListType::iterator end() const {
return this->list.end();
}
typename ListType::iterator find(typename ListType::const_reference ref) const {
return this->list.find(ref);
}
};
friend class ListAccessor;
private:
KLightLock lock;
ListType object_list;
public:
constexpr KAutoObjectWithListContainer() : lock(), object_list() { MESOSPHERE_ASSERT_THIS(); }
void Initialize() { MESOSPHERE_ASSERT_THIS(); }
void Finalize() { MESOSPHERE_ASSERT_THIS(); }
Result Register(KAutoObjectWithList *obj);
Result Unregister(KAutoObjectWithList *obj);
size_t GetOwnedCount(KProcess *owner);
};
}

View File

@@ -0,0 +1,280 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_select_page_table.hpp>
#include <mesosphere/kern_svc.hpp>
namespace ams::kern {
class KCapabilities {
private:
static constexpr size_t SvcFlagCount = svc::NumSupervisorCalls / BITSIZEOF(u8);
static constexpr size_t IrqFlagCount = /* TODO */0x80;
enum class CapabilityType : u32 {
CorePriority = (1u << 3) - 1,
SyscallMask = (1u << 4) - 1,
MapRange = (1u << 6) - 1,
MapIoPage = (1u << 7) - 1,
MapRegion = (1u << 10) - 1,
InterruptPair = (1u << 11) - 1,
ProgramType = (1u << 13) - 1,
KernelVersion = (1u << 14) - 1,
HandleTable = (1u << 15) - 1,
DebugFlags = (1u << 16) - 1,
Invalid = 0u,
Padding = ~0u,
};
using RawCapabilityValue = util::BitPack32::Field<0, BITSIZEOF(util::BitPack32), u32>;
static constexpr CapabilityType GetCapabilityType(const util::BitPack32 cap) {
const u32 value = cap.Get<RawCapabilityValue>();
return static_cast<CapabilityType>((~value & (value + 1)) - 1);
}
static constexpr u32 GetCapabilityFlag(CapabilityType type) {
return static_cast<u32>(type) + 1;
}
static constexpr u32 CountTrailingZero(u32 flag) {
for (u32 i = 0; i < BITSIZEOF(u32); i++) {
if (flag & (1u << i)) {
return i;
}
}
return BITSIZEOF(u32);
}
static constexpr u32 GetCapabilityId(CapabilityType type) {
const u32 flag = GetCapabilityFlag(type);
if (true /* C++20: std::is_constant_evaluated() */) {
return CountTrailingZero(flag);
} else {
return static_cast<u32>(__builtin_ctz(flag));
}
}
template<size_t Index, size_t Count, typename T = u32>
using Field = util::BitPack32::Field<Index, Count, T>;
#define DEFINE_FIELD(name, prev, ...) using name = Field<prev::Next, __VA_ARGS__>
template<CapabilityType Type>
static constexpr inline u32 CapabilityFlag = []() -> u32 {
return static_cast<u32>(Type) + 1;
}();
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() */) {
for (u32 i = 0; i < BITSIZEOF(u32); i++) {
if (flag & (1u << i)) {
return i;
}
}
return BITSIZEOF(u32);
} else {
return __builtin_ctz(flag);
}
}();
struct CorePriority {
using IdBits = Field<0, CapabilityId<CapabilityType::CorePriority> + 1>;
DEFINE_FIELD(LowestThreadPriority, IdBits, 6);
DEFINE_FIELD(HighestThreadPriority, LowestThreadPriority, 6);
DEFINE_FIELD(MinimumCoreId, HighestThreadPriority, 8);
DEFINE_FIELD(MaximumCoreId, MinimumCoreId, 8);
};
struct SyscallMask {
using IdBits = Field<0, CapabilityId<CapabilityType::SyscallMask> + 1>;
DEFINE_FIELD(Mask, IdBits, 24);
DEFINE_FIELD(Index, Mask, 3);
};
static constexpr u64 PhysicalMapAllowedMask = (1ul << 36) - 1;
struct MapRange {
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
DEFINE_FIELD(Address, IdBits, 24);
DEFINE_FIELD(ReadOnly, Address, 1, bool);
};
struct MapRangeSize {
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
DEFINE_FIELD(Pages, IdBits, 20);
DEFINE_FIELD(Reserved, Pages, 4);
DEFINE_FIELD(Normal, Reserved, 1, bool);
};
struct MapIoPage {
using IdBits = Field<0, CapabilityId<CapabilityType::MapIoPage> + 1>;
DEFINE_FIELD(Address, IdBits, 24);
};
enum class RegionType : u32 {
None = 0,
KernelTraceBuffer = 1,
OnMemoryBootImage = 2,
DTB = 3,
};
struct MapRegion {
using IdBits = Field<0, CapabilityId<CapabilityType::MapRegion> + 1>;
DEFINE_FIELD(Region0, IdBits, 6, RegionType);
DEFINE_FIELD(ReadOnly0, Region0, 1, bool);
DEFINE_FIELD(Region1, ReadOnly0, 6, RegionType);
DEFINE_FIELD(ReadOnly1, Region1, 1, bool);
DEFINE_FIELD(Region2, ReadOnly1, 6, RegionType);
DEFINE_FIELD(ReadOnly2, Region2, 1, bool);
};
static const u32 PaddingInterruptId = 0x3FF;
struct InterruptPair {
using IdBits = Field<0, CapabilityId<CapabilityType::InterruptPair> + 1>;
DEFINE_FIELD(InterruptId0, IdBits, 10);
DEFINE_FIELD(InterruptId1, InterruptId0, 10);
};
struct ProgramType {
using IdBits = Field<0, CapabilityId<CapabilityType::ProgramType> + 1>;
DEFINE_FIELD(Type, IdBits, 3);
DEFINE_FIELD(Reserved, Type, 15);
};
struct KernelVersion {
using IdBits = Field<0, CapabilityId<CapabilityType::KernelVersion> + 1>;
DEFINE_FIELD(MinorVersion, IdBits, 4);
DEFINE_FIELD(MajorVersion, MinorVersion, 13);
};
struct HandleTable {
using IdBits = Field<0, CapabilityId<CapabilityType::HandleTable> + 1>;
DEFINE_FIELD(Size, IdBits, 10);
DEFINE_FIELD(Reserved, Size, 6);
};
struct DebugFlags {
using IdBits = Field<0, CapabilityId<CapabilityType::DebugFlags> + 1>;
DEFINE_FIELD(AllowDebug, IdBits, 1, bool);
DEFINE_FIELD(ForceDebug, AllowDebug, 1, bool);
DEFINE_FIELD(Reserved, ForceDebug, 13);
};
#undef DEFINE_FIELD
static constexpr u32 InitializeOnceFlags = CapabilityFlag<CapabilityType::CorePriority> |
CapabilityFlag<CapabilityType::ProgramType> |
CapabilityFlag<CapabilityType::KernelVersion> |
CapabilityFlag<CapabilityType::HandleTable> |
CapabilityFlag<CapabilityType::DebugFlags>;
private:
u8 svc_access_flags[SvcFlagCount]{};
u8 irq_access_flags[IrqFlagCount]{};
u64 core_mask{};
u64 priority_mask{};
util::BitPack32 debug_capabilities;
s32 handle_table_size{};
util::BitPack32 intended_kernel_version;
u32 program_type{};
private:
static constexpr ALWAYS_INLINE void SetSvcAllowedImpl(u8 *data, u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(*data);
MESOSPHERE_ASSERT(id < svc::SvcId_Count);
data[id / BitsPerWord] |= (1ul << (id % BitsPerWord));
}
static constexpr ALWAYS_INLINE void ClearSvcAllowedImpl(u8 *data, u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(*data);
MESOSPHERE_ASSERT(id < svc::SvcId_Count);
data[id / BitsPerWord] &= ~(1ul << (id % BitsPerWord));
}
bool SetSvcAllowed(u32 id) {
if (id < BITSIZEOF(this->svc_access_flags)) {
SetSvcAllowedImpl(this->svc_access_flags, id);
return true;
} else {
return false;
}
}
bool SetInterruptAllowed(u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(this->irq_access_flags[0]);
if (id < BITSIZEOF(this->irq_access_flags)) {
this->irq_access_flags[id / BitsPerWord] = (1ul << (id % BitsPerWord));
return true;
} else {
return false;
}
}
Result SetCorePriorityCapability(const util::BitPack32 cap);
Result SetSyscallMaskCapability(const util::BitPack32 cap, u32 &set_svc);
Result MapRange(const util::BitPack32 cap, const util::BitPack32 size_cap, KProcessPageTable *page_table);
Result MapIoPage(const util::BitPack32 cap, KProcessPageTable *page_table);
Result MapRegion(const util::BitPack32 cap, KProcessPageTable *page_table);
Result SetInterruptPairCapability(const util::BitPack32 cap);
Result SetProgramTypeCapability(const util::BitPack32 cap);
Result SetKernelVersionCapability(const util::BitPack32 cap);
Result SetHandleTableCapability(const util::BitPack32 cap);
Result SetDebugFlagsCapability(const util::BitPack32 cap);
Result SetCapability(const util::BitPack32 cap, u32 &set_flags, u32 &set_svc, KProcessPageTable *page_table);
Result SetCapabilities(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
public:
constexpr KCapabilities() : debug_capabilities(0), intended_kernel_version(0) { /* ... */ }
Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
constexpr u64 GetCoreMask() const { return this->core_mask; }
constexpr u64 GetPriorityMask() const { return this->priority_mask; }
constexpr s32 GetHandleTableSize() const { return this->handle_table_size; }
ALWAYS_INLINE void CopySvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
std::memcpy(sp.svc_permission, this->svc_access_flags, sizeof(this->svc_access_flags));
/* Clear specific SVCs based on our state. */
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState);
if (sp.is_preemption_state_pinned) {
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
}
}
/* TODO: Member functions. */
};
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern {
class KAutoObject;
class KClassTokenGenerator {
public:
using TokenBaseType = u16;
public:
static constexpr size_t BaseClassBits = 8;
static constexpr size_t FinalClassBits = (sizeof(TokenBaseType) * CHAR_BIT) - BaseClassBits;
/* One bit per base class. */
static constexpr size_t NumBaseClasses = BaseClassBits;
/* Final classes are permutations of three bits. */
static constexpr size_t NumFinalClasses = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
index++;
}
}
}
return index;
}();
private:
template<TokenBaseType Index>
static constexpr inline TokenBaseType BaseClassToken = BIT(Index);
template<TokenBaseType Index>
static constexpr inline TokenBaseType FinalClassToken = [] {
TokenBaseType index = 0;
for (size_t i = 0; i < FinalClassBits; i++) {
for (size_t j = i + 1; j < FinalClassBits; j++) {
for (size_t k = j + 1; k < FinalClassBits; k++) {
if ((index++) == Index) {
return ((1ul << i) | (1ul << j) | (1ul << k)) << BaseClassBits;
}
}
}
}
__builtin_unreachable();
}();
template<typename T>
static constexpr inline TokenBaseType GetClassToken() {
static_assert(std::is_base_of<KAutoObject, T>::value);
if constexpr (std::is_same<T, KAutoObject>::value) {
static_assert(T::ObjectType == ObjectType::KAutoObject);
return 0;
} else if constexpr (!std::is_final<T>::value) {
static_assert(ObjectType::BaseClassesStart <= T::ObjectType && T::ObjectType < ObjectType::BaseClassesEnd);
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) - static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else if constexpr (ObjectType::FinalClassesStart <= T::ObjectType && T::ObjectType < ObjectType::FinalClassesEnd) {
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) - static_cast<TokenBaseType>(ObjectType::FinalClassesStart);
return FinalClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
} else {
static_assert(!std::is_same<T, T>::value, "GetClassToken: Invalid Type");
}
};
public:
enum class ObjectType {
KAutoObject,
BaseClassesStart,
KSynchronizationObject = BaseClassesStart,
KReadableEvent,
BaseClassesEnd,
FinalClassesStart = BaseClassesEnd,
KInterruptEvent = FinalClassesStart,
KDebug,
KThread,
KServerPort,
KServerSession,
KClientPort,
KClientSession,
KProcess,
KResourceLimit,
KLightSession,
KPort,
KSession,
KSharedMemory,
KEvent,
KWritableEvent,
KLightClientSession,
KLightServerSession,
KTransferMemory,
KDeviceAddressSpace,
KSessionRequest,
KCodeMemory,
FinalClassesEnd = FinalClassesStart + NumFinalClasses,
};
template<typename T>
static constexpr inline TokenBaseType ClassToken = GetClassToken<T>();
};
using ClassTokenType = KClassTokenGenerator::TokenBaseType;
template<typename T>
static constexpr inline ClassTokenType ClassToken = KClassTokenGenerator::ClassToken<T>;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KPort;
class KClientPort final : public KSynchronizationObject {
MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
private:
std::atomic<s32> num_sessions;
std::atomic<s32> peak_sessions;
s32 max_sessions;
KPort *parent;
public:
constexpr KClientPort() : num_sessions(), peak_sessions(), max_sessions(), parent() { /* ... */ }
virtual ~KClientPort() { /* ... */ }
void Initialize(KPort *parent, s32 max_sessions);
constexpr const KPort *GetParent() const { return this->parent; }
bool IsLight() const;
/* Overridden virtual functions. */
virtual void Destroy() override;
virtual bool IsSignaled() const override;
/* TODO: More of KClientPort. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KSession;
class KClientSession final : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
private:
KSession *parent;
public:
constexpr KClientSession() : parent() { /* ... */ }
virtual ~KClientSession() { /* ... */ }
void Initialize(KSession *parent) {
/* Set member variables. */
this->parent = parent;
}
static void PostDestroy(uintptr_t arg) { /* ... */ }
constexpr const KSession *GetParent() const { return this->parent; }
/* TODO: More of KClientSession. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_k_scheduler.hpp>
namespace ams::kern {
struct KConditionVariableComparator {
static constexpr ALWAYS_INLINE int Compare(const KThread &lhs, const KThread &rhs) {
const uintptr_t l_key = lhs.GetConditionVariableKey();
const uintptr_t r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) {
/* Sort first by key */
return -1;
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
/* And then by priority. */
return -1;
} else {
return 1;
}
}
};
class KConditionVariable {
public:
using ThreadTree = util::IntrusiveRedBlackTreeMemberTraits<&KThread::condvar_arbiter_tree_node>::TreeType<KConditionVariableComparator>;
private:
ThreadTree tree;
public:
constexpr KConditionVariable() : tree() { /* ... */ }
/* Arbitration. */
Result SignalToAddress(KProcessAddress addr);
Result WaitForAddress(ams::svc::Handle handle, KProcessAddress addr, u32 value);
/* Condition variable. */
void Signal(uintptr_t cv_key, s32 count);
Result Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout);
ALWAYS_INLINE void BeforeUpdatePriority(KThread *thread) {
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
this->tree.erase(this->tree.iterator_to(*thread));
}
ALWAYS_INLINE void AfterUpdatePriority(KThread *thread) {
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
this->tree.insert(*thread);
}
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_current_context.hpp>
#include <mesosphere/kern_k_scheduler.hpp>
#include <mesosphere/kern_k_interrupt_task_manager.hpp>
#include <mesosphere/kern_select_interrupt_manager.hpp>
#include <mesosphere/kern_select_hardware_timer.hpp>
#include <mesosphere/kern_k_memory_manager.hpp>
namespace ams::kern {
struct KCoreLocalContext {
KCurrentContext current;
KScheduler scheduler;
KInterruptTaskManager interrupt_task_manager;
KInterruptManager interrupt_manager;
KHardwareTimer hardware_timer;
/* Everything after this point is for debugging. */
/* Retail kernel doesn't even consistently update these fields. */
u64 num_sw_interrupts;
u64 num_hw_interrupts;
std::atomic<u64> num_svc;
u64 num_process_switches;
u64 num_thread_switches;
u64 num_fpu_switches;
u64 num_scheduler_updates;
u64 num_invoked_scheduler_updates;
std::atomic<u64> num_specific_svc[0x80];
u32 perf_counters[6];
};
static_assert(sizeof(KCoreLocalContext) < PageSize);
struct KCoreLocalPage {
KCoreLocalContext context;
u8 padding[PageSize - sizeof(KCoreLocalContext)];
};
static_assert(sizeof(KCoreLocalPage) == PageSize);
struct KCoreLocalRegion {
KCoreLocalPage current;
KCoreLocalPage absolute[cpu::NumCores];
};
static_assert(sizeof(KCoreLocalRegion) == PageSize * (1 + cpu::NumCores));
}

View File

@@ -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 <mesosphere/kern_select_cpu.hpp>
namespace ams::kern {
class KThread;
class KProcess;
class KScheduler;
class KInterruptTaskManager;
struct KCurrentContext {
std::atomic<KThread *> current_thread;
std::atomic<KProcess *> current_process;
KScheduler *scheduler;
KInterruptTaskManager *interrupt_task_manager;
s32 core_id;
void *exception_stack_top;
};
static_assert(std::is_pod<KCurrentContext>::value);
static_assert(sizeof(KCurrentContext) <= cpu::DataCacheLineSize);
namespace impl {
ALWAYS_INLINE KCurrentContext &GetCurrentContext() {
return *reinterpret_cast<KCurrentContext *>(cpu::GetCoreLocalRegionAddress());
}
}
ALWAYS_INLINE KThread *GetCurrentThreadPointer() {
return impl::GetCurrentContext().current_thread.load(std::memory_order_relaxed);
}
ALWAYS_INLINE KThread &GetCurrentThread() {
return *GetCurrentThreadPointer();
}
ALWAYS_INLINE KProcess *GetCurrentProcessPointer() {
return impl::GetCurrentContext().current_process.load(std::memory_order_relaxed);
}
ALWAYS_INLINE KProcess &GetCurrentProcess() {
return *GetCurrentProcessPointer();
}
ALWAYS_INLINE KScheduler *GetCurrentSchedulerPointer() {
return impl::GetCurrentContext().scheduler;
}
ALWAYS_INLINE KScheduler &GetCurrentScheduler() {
return *GetCurrentSchedulerPointer();
}
ALWAYS_INLINE KInterruptTaskManager *GetCurrentInterruptTaskManagerPointer() {
return impl::GetCurrentContext().interrupt_task_manager;
}
ALWAYS_INLINE KInterruptTaskManager &GetCurrentInterruptTaskManager() {
return *GetCurrentInterruptTaskManagerPointer();
}
ALWAYS_INLINE s32 GetCurrentCoreId() {
return impl::GetCurrentContext().core_id;
}
ALWAYS_INLINE void SetCurrentThread(KThread *new_thread) {
impl::GetCurrentContext().current_thread = new_thread;
}
ALWAYS_INLINE void SetCurrentProcess(KProcess *new_process) {
impl::GetCurrentContext().current_process = new_process;
}
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KDebugBase : public KSynchronizationObject {
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_select_device_page_table.hpp>
namespace ams::kern {
class KDeviceAddressSpace final : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
public:
static void Initialize();
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
namespace ams::kern {
class KDpcManager {
private:
static constexpr s32 DpcManagerNormalThreadPriority = 59;
static constexpr s32 DpcManagerPreemptionThreadPriority = 63;
static_assert(ams::svc::HighestThreadPriority <= DpcManagerNormalThreadPriority && DpcManagerNormalThreadPriority <= ams::svc::LowestThreadPriority);
static_assert(ams::svc::HighestThreadPriority <= DpcManagerPreemptionThreadPriority && DpcManagerPreemptionThreadPriority <= ams::svc::LowestThreadPriority);
private:
static NOINLINE void Initialize(s32 core_id, s32 priority);
public:
static void Initialize() {
const s32 core_id = GetCurrentCoreId();
if (core_id == static_cast<s32>(cpu::NumCores) - 1) {
Initialize(core_id, DpcManagerPreemptionThreadPriority);
} else {
Initialize(core_id, DpcManagerNormalThreadPriority);
}
}
static NOINLINE void HandleDpc();
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_slab_heap.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_memory_block.hpp>
namespace ams::kern {
namespace impl {
class DynamicSlabHeapPage {
private:
u8 buffer[PageSize];
};
static_assert(sizeof(DynamicSlabHeapPage) == PageSize);
};
template<typename T>
class KDynamicSlabHeap {
NON_COPYABLE(KDynamicSlabHeap);
NON_MOVEABLE(KDynamicSlabHeap);
private:
using Impl = impl::KSlabHeapImpl;
using PageBuffer = impl::DynamicSlabHeapPage;
private:
Impl impl;
KDynamicSlabHeap<PageBuffer> *next_allocator;
std::atomic<size_t> used;
std::atomic<size_t> peak;
std::atomic<size_t> count;
KVirtualAddress address;
size_t size;
private:
ALWAYS_INLINE Impl *GetImpl() {
return std::addressof(this->impl);
}
ALWAYS_INLINE const Impl *GetImpl() const {
return std::addressof(this->impl);
}
public:
constexpr KDynamicSlabHeap() : impl(), next_allocator(), used(), peak(), count(), address(), size() { /* ... */ }
constexpr KVirtualAddress GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
constexpr size_t GetUsed() const { return this->used; }
constexpr size_t GetPeak() const { return this->peak; }
constexpr size_t GetCount() const { return this->count; }
constexpr bool IsInRange(KVirtualAddress addr) const {
return this->GetAddress() <= addr && addr <= this->GetAddress() + this->GetSize() - 1;
}
void Initialize(KVirtualAddress memory, size_t sz) {
/* Set tracking fields. */
this->address = memory;
this->count = sz / sizeof(T);
this->size = this->count * sizeof(T);
/* Free blocks to memory. */
u8 *cur = GetPointer<u8>(this->address + this->size);
for (size_t i = 0; i < this->count; i++) {
cur -= sizeof(T);
this->GetImpl()->Free(cur);
}
}
void Initialize(KDynamicSlabHeap<PageBuffer> *next) {
this->next_allocator = next;
this->address = next->GetAddress();
this->size = next->GetSize();
}
T *Allocate() {
T *allocated = reinterpret_cast<T *>(this->GetImpl()->Allocate());
/* If we fail to allocate, try to get a new page from our next allocator. */
if (AMS_UNLIKELY(allocated == nullptr)) {
if (this->next_allocator != nullptr) {
allocated = reinterpret_cast<T *>(this->next_allocator->Allocate());
if (allocated != nullptr) {
/* If we succeeded in getting a page, free the rest to our slab. */
for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
this->GetImpl()->Free(allocated + i);
}
this->count += sizeof(PageBuffer) / sizeof(T);
}
}
}
if (AMS_LIKELY(allocated != nullptr)) {
/* Construct the object. */
new (allocated) T();
/* Update our tracking. */
size_t used = ++this->used;
size_t peak = this->peak;
while (peak < used) {
if (this->peak.compare_exchange_weak(peak, used, std::memory_order_relaxed)) {
break;
}
}
}
return allocated;
}
void Free(T *t) {
this->GetImpl()->Free(t);
--this->used;
}
};
class KDynamicPageManager : public KDynamicSlabHeap<impl::DynamicSlabHeapPage>{};
class KBlockInfoManager : public KDynamicSlabHeap<KBlockInfo>{};
class KMemoryBlockSlabManager : public KDynamicSlabHeap<KMemoryBlock>{};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_readable_event.hpp>
namespace ams::kern {
class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KEvent, KAutoObject);
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KEventInfo : public KSlabAllocated<KEventInfo>, public util::IntrusiveListBaseNode<KEventInfo> {
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -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
#ifdef ATMOSPHERE_ARCH_ARM64
#include <mesosphere/arch/arm64/kern_k_exception_context.hpp>
namespace ams::kern {
using ams::kern::arch::arm64::KExceptionContext;
}
#else
#error "Unknown architecture for KExceptionContext"
#endif

View File

@@ -0,0 +1,358 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_k_interrupt_event.hpp>
namespace ams::kern {
constexpr ALWAYS_INLINE util::BitPack32 GetHandleBitPack(ams::svc::Handle handle) {
return util::BitPack32{handle};
}
class KProcess;
class KThread;
class KHandleTable {
NON_COPYABLE(KHandleTable);
NON_MOVEABLE(KHandleTable);
public:
static constexpr size_t MaxTableSize = 1024;
private:
using HandleRawValue = util::BitPack32::Field<0, BITSIZEOF(u32), u32>;
using HandleEncoded = util::BitPack32::Field<0, BITSIZEOF(ams::svc::Handle), ams::svc::Handle>;
using HandleIndex = util::BitPack32::Field<0, 15, u16>;
using HandleLinearId = util::BitPack32::Field<HandleIndex::Next, 15, u16>;
using HandleReserved = util::BitPack32::Field<HandleLinearId::Next, 2, u32>;
static constexpr u16 MinLinearId = 1;
static constexpr u16 MaxLinearId = util::BitPack32{std::numeric_limits<u32>::max()}.Get<HandleLinearId>();
static constexpr ALWAYS_INLINE ams::svc::Handle EncodeHandle(u16 index, u16 linear_id) {
util::BitPack32 pack = {0};
pack.Set<HandleIndex>(index);
pack.Set<HandleLinearId>(linear_id);
pack.Set<HandleReserved>(0);
return pack.Get<HandleEncoded>();
}
class Entry {
private:
union {
struct {
u16 linear_id;
u16 type;
} info;
Entry *next_free_entry;
} meta;
KAutoObject *object;
public:
constexpr Entry() : meta(), object(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE void SetFree(Entry *next) {
this->object = nullptr;
this->meta.next_free_entry = next;
}
constexpr ALWAYS_INLINE void SetUsed(KAutoObject *obj, u16 linear_id, u16 type) {
this->object = obj;
this->meta.info = { linear_id, type };
}
constexpr ALWAYS_INLINE KAutoObject *GetObject() const { return this->object; }
constexpr ALWAYS_INLINE Entry *GetNextFreeEntry() const { return this->meta.next_free_entry; }
constexpr ALWAYS_INLINE u16 GetLinearId() const { return this->meta.info.linear_id; }
constexpr ALWAYS_INLINE u16 GetType() const { return this->meta.info.type; }
};
private:
mutable KSpinLock lock;
Entry *table;
Entry *free_head;
Entry entries[MaxTableSize];
u16 table_size;
u16 max_count;
u16 next_linear_id;
u16 count;
public:
constexpr KHandleTable() :
lock(), table(nullptr), free_head(nullptr), entries(), table_size(0), max_count(0), next_linear_id(MinLinearId), count(0)
{ MESOSPHERE_ASSERT_THIS(); }
constexpr NOINLINE Result Initialize(s32 size) {
MESOSPHERE_ASSERT_THIS();
R_UNLESS(size <= static_cast<s32>(MaxTableSize), svc::ResultOutOfMemory());
/* Initialize all fields. */
this->table = this->entries;
this->table_size = (size <= 0) ? MaxTableSize : size;
this->next_linear_id = MinLinearId;
this->count = 0;
this->max_count = 0;
/* Free all entries. */
for (size_t i = 0; i < static_cast<size_t>(this->table_size - 1); i++) {
this->entries[i].SetFree(std::addressof(this->entries[i + 1]));
}
this->entries[this->table_size - 1].SetFree(nullptr);
this->free_head = std::addressof(this->entries[0]);
return ResultSuccess();
}
constexpr ALWAYS_INLINE size_t GetTableSize() const { return this->table_size; }
constexpr ALWAYS_INLINE size_t GetCount() const { return this->count; }
constexpr ALWAYS_INLINE size_t GetMaxCount() const { return this->max_count; }
NOINLINE Result Finalize();
NOINLINE bool Remove(ams::svc::Handle handle);
template<typename T = KAutoObject>
ALWAYS_INLINE KScopedAutoObject<T> GetObject(ams::svc::Handle handle) const {
MESOSPHERE_ASSERT_THIS();
/* Handle pseudo-handles. */
if constexpr (std::is_base_of<T, KProcess>::value) {
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
return GetCurrentProcessPointer();
}
} else if constexpr (std::is_base_of<T, KThread>::value) {
if (handle == ams::svc::PseudoHandle::CurrentThread) {
return GetCurrentThreadPointer();
}
}
/* Lock and look up in table. */
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->lock);
if constexpr (std::is_same<T, KAutoObject>::value) {
return this->GetObjectImpl(handle);
} else {
return this->GetObjectImpl(handle)->DynamicCast<T*>();
}
}
template<typename T = KAutoObject>
ALWAYS_INLINE KScopedAutoObject<T> GetObjectForIpc(ams::svc::Handle handle) const {
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
/* Handle pseudo-handles. */
if constexpr (std::is_base_of<T, KProcess>::value) {
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
return GetCurrentProcessPointer();
}
} else if constexpr (std::is_base_of<T, KThread>::value) {
if (handle == ams::svc::PseudoHandle::CurrentThread) {
return GetCurrentThreadPointer();
}
}
/* Lock and look up in table. */
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->lock);
KAutoObject *obj = this->GetObjectImpl(handle);
if (obj->DynamicCast<KInterruptEvent *>() != nullptr) {
return nullptr;
}
if constexpr (std::is_same<T, KAutoObject>::value) {
return obj;
} else {
return obj->DynamicCast<T*>();
}
}
ALWAYS_INLINE KScopedAutoObject<KAutoObject> GetObjectByIndex(ams::svc::Handle *out_handle, size_t index) const {
MESOSPHERE_ASSERT_THIS();
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->lock);
return this->GetObjectByIndexImpl(out_handle, index);
}
NOINLINE Result Reserve(ams::svc::Handle *out_handle);
NOINLINE void Unreserve(ams::svc::Handle handle);
template<typename T>
ALWAYS_INLINE Result Add(ams::svc::Handle *out_handle, T *obj) {
static_assert(std::is_base_of<KAutoObject, T>::value);
return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
}
template<typename T>
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
static_assert(std::is_base_of<KAutoObject, T>::value);
return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
}
private:
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type);
constexpr ALWAYS_INLINE Entry *AllocateEntry() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->count < this->table_size);
Entry *entry = this->free_head;
this->free_head = entry->GetNextFreeEntry();
this->count++;
this->max_count = std::max(this->max_count, this->count);
return entry;
}
constexpr ALWAYS_INLINE void FreeEntry(Entry *entry) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->count > 0);
entry->SetFree(this->free_head);
this->free_head = entry;
this->count--;
}
constexpr ALWAYS_INLINE u16 AllocateLinearId() {
const u16 id = this->next_linear_id++;
if (this->next_linear_id > MaxLinearId) {
this->next_linear_id = MinLinearId;
}
return id;
}
constexpr ALWAYS_INLINE size_t GetEntryIndex(Entry *entry) {
const size_t index = entry - this->table;
MESOSPHERE_ASSERT(index < this->table_size);
return index;
}
constexpr ALWAYS_INLINE Entry *FindEntry(ams::svc::Handle handle) const {
MESOSPHERE_ASSERT_THIS();
/* Unpack the handle. */
const auto handle_pack = GetHandleBitPack(handle);
const auto raw_value = handle_pack.Get<HandleRawValue>();
const auto index = handle_pack.Get<HandleIndex>();
const auto linear_id = handle_pack.Get<HandleLinearId>();
const auto reserved = handle_pack.Get<HandleReserved>();
MESOSPHERE_ASSERT(reserved == 0);
/* Validate our indexing information. */
if (raw_value == 0) {
return nullptr;
}
if (linear_id == 0) {
return nullptr;
}
if (index >= this->table_size) {
return nullptr;
}
/* Get the entry, and ensure our serial id is correct. */
Entry *entry = std::addressof(this->table[index]);
if (entry->GetObject() == nullptr) {
return nullptr;
}
if (entry->GetLinearId() != linear_id) {
return nullptr;
}
return entry;
}
constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
MESOSPHERE_ASSERT_THIS();
/* Handles must not have reserved bits set. */
if (GetHandleBitPack(handle).Get<HandleReserved>() != 0) {
return nullptr;
}
if (Entry *entry = this->FindEntry(handle); entry != nullptr) {
return entry->GetObject();
} else {
return nullptr;
}
}
constexpr ALWAYS_INLINE KAutoObject *GetObjectByIndexImpl(ams::svc::Handle *out_handle, size_t index) const {
MESOSPHERE_ASSERT_THIS();
/* Index must be in bounds. */
if (index >= this->table_size || this->table == nullptr) {
return nullptr;
}
/* Ensure entry has an object. */
Entry *entry = std::addressof(this->table[index]);
if (entry->GetObject() == nullptr) {
return nullptr;
}
*out_handle = EncodeHandle(index, entry->GetLinearId());
return entry->GetObject();
}
template<typename T>
ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const {
/* Try to convert and open all the handles. */
size_t num_opened;
{
/* Lock the table. */
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
/* Get the current handle. */
const auto cur_handle = handles[num_opened];
/* Get the object for the current handle. */
KAutoObject *cur_object = this->GetObjectImpl(cur_handle);
if (AMS_UNLIKELY(cur_object == nullptr)) {
break;
}
/* Cast the current object to the desired type. */
T *cur_t = cur_object->DynamicCast<T*>();
if (AMS_UNLIKELY(cur_t == nullptr)) {
break;
}
/* Open a reference to the current object. */
cur_t->Open();
out[num_opened] = cur_t;
}
}
/* If we converted every object, succeed. */
if (AMS_LIKELY(num_opened == num_handles)) {
return true;
}
/* If we didn't convert entry object, close the ones we opened. */
for (size_t i = 0; i < num_opened; i++) {
out[i]->Close();
}
return false;
}
};
}

View File

@@ -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 <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_timer_task.hpp>
#include <mesosphere/kern_select_interrupt_manager.hpp>
namespace ams::kern {
class KHardwareTimerBase {
private:
using TimerTaskTree = util::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
private:
KSpinLock lock;
TimerTaskTree task_tree;
KTimerTask *next_task;
public:
constexpr ALWAYS_INLINE KHardwareTimerBase() : lock(), task_tree(), next_task(nullptr) { /* ... */ }
private:
ALWAYS_INLINE void RemoveTaskFromTree(KTimerTask *task) {
/* Erase from the tree. */
auto it = this->task_tree.erase(this->task_tree.iterator_to(*task));
/* Clear the task's scheduled time. */
task->SetTime(0);
/* Update our next task if relevant. */
if (this->next_task == task) {
this->next_task = (it != this->task_tree.end()) ? std::addressof(*it) : nullptr;
}
}
public:
NOINLINE void CancelTask(KTimerTask *task) {
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->lock);
if (const s64 task_time = task->GetTime(); task_time > 0) {
this->RemoveTaskFromTree(task);
}
}
protected:
ALWAYS_INLINE KSpinLock &GetLock() { return this->lock; }
ALWAYS_INLINE s64 DoInterruptTaskImpl(s64 cur_time) {
/* We want to handle all tasks, returning the next time that a task is scheduled. */
while (true) {
/* Get the next task. If there isn't one, return 0. */
KTimerTask *task = this->next_task;
if (task == nullptr) {
return 0;
}
/* If the task needs to be done in the future, do it in the future and not now. */
if (const s64 task_time = task->GetTime(); task_time > cur_time) {
return task_time;
}
/* Remove the task from the tree of tasks, and update our next task. */
this->RemoveTaskFromTree(task);
/* Handle the task. */
task->OnTimer();
}
}
ALWAYS_INLINE bool RegisterAbsoluteTaskImpl(KTimerTask *task, s64 task_time) {
MESOSPHERE_ASSERT(task_time > 0);
/* Set the task's time, and insert it into our tree. */
task->SetTime(task_time);
this->task_tree.insert(*task);
/* Update our next task if relevant. */
if (this->next_task != nullptr && this->next_task->GetTime() <= task_time) {
return false;
}
this->next_task = task;
return true;
}
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_address_space_info.hpp>
#include <mesosphere/kern_select_page_table.hpp>
namespace ams::kern {
class KInitialProcessHeader {
private:
static constexpr u32 Magic = util::FourCC<'K','I','P','1'>::Code;
private:
u32 magic;
u8 name[12];
u64 program_id;
u32 version;
u8 priority;
u8 ideal_core_id;
u8 _1E;
u8 flags;
u32 rx_address;
u32 rx_size;
u32 rx_compressed_size;
u32 affinity_mask;
u32 ro_address;
u32 ro_size;
u32 ro_compressed_size;
u32 stack_size;
u32 rw_address;
u32 rw_size;
u32 rw_compressed_size;
u32 _4C;
u32 bss_address;
u32 bss_size;
u32 pad[(0x80 - 0x58) / sizeof(u32)];
u32 capabilities[0x80 / sizeof(u32)];
public:
constexpr bool IsValid() const { return this->magic == Magic; }
constexpr void GetName(char *dst, size_t size) const {
std::memset(dst, 0, size);
std::memcpy(dst, this->name, std::min(sizeof(this->name), size));
}
constexpr const u32 *GetCapabilities() const { return this->capabilities; }
constexpr size_t GetNumCapabilities() const { return util::size(this->capabilities); }
constexpr u64 GetProgramId() const { return this->program_id; }
constexpr u32 GetVersion() const { return this->version; }
constexpr u8 GetPriority() const { return this->priority; }
constexpr u8 GetIdealCoreId() const { return this->ideal_core_id; }
constexpr bool IsRxCompressed() const { return (this->flags & (1 << 0)); }
constexpr bool IsRoCompressed() const { return (this->flags & (1 << 1)); }
constexpr bool IsRwCompressed() const { return (this->flags & (1 << 2)); }
constexpr bool Is64Bit() const { return (this->flags & (1 << 3)); }
constexpr bool Is64BitAddressSpace() const { return (this->flags & (1 << 4)); }
constexpr bool UsesSecureMemory() const { return (this->flags & (1 << 5)); }
constexpr u32 GetRxAddress() const { return this->rx_address; }
constexpr u32 GetRxSize() const { return this->rx_size; }
constexpr u32 GetRxCompressedSize() const { return this->rx_compressed_size; }
constexpr u32 GetRoAddress() const { return this->ro_address; }
constexpr u32 GetRoSize() const { return this->ro_size; }
constexpr u32 GetRoCompressedSize() const { return this->ro_compressed_size; }
constexpr u32 GetRwAddress() const { return this->rw_address; }
constexpr u32 GetRwSize() const { return this->rw_size; }
constexpr u32 GetRwCompressedSize() const { return this->rw_compressed_size; }
constexpr u32 GetBssAddress() const { return this->bss_address; }
constexpr u32 GetBssSize() const { return this->bss_size; }
constexpr u32 GetAffinityMask() const { return this->affinity_mask; }
constexpr u32 GetStackSize() const { return this->stack_size; }
};
static_assert(sizeof(KInitialProcessHeader) == 0x100);
class KInitialProcessReader {
private:
KInitialProcessHeader *kip_header;
public:
constexpr KInitialProcessReader() : kip_header() { /* ... */ }
constexpr const u32 *GetCapabilities() const { return this->kip_header->GetCapabilities(); }
constexpr size_t GetNumCapabilities() const { return this->kip_header->GetNumCapabilities(); }
constexpr size_t GetBinarySize() const {
return sizeof(*kip_header) + this->kip_header->GetRxCompressedSize() + this->kip_header->GetRoCompressedSize() + this->kip_header->GetRwCompressedSize();
}
constexpr size_t GetSize() const {
if (const size_t bss_size = this->kip_header->GetBssSize(); bss_size != 0) {
return this->kip_header->GetBssAddress() + this->kip_header->GetBssSize();
} else {
return this->kip_header->GetRwAddress() + this->kip_header->GetRwSize();
}
}
constexpr u8 GetPriority() const { return this->kip_header->GetPriority(); }
constexpr u8 GetIdealCoreId() const { return this->kip_header->GetIdealCoreId(); }
constexpr u32 GetAffinityMask() const { return this->kip_header->GetAffinityMask(); }
constexpr u32 GetStackSize() const { return this->kip_header->GetStackSize(); }
constexpr bool Is64Bit() const { return this->kip_header->Is64Bit(); }
constexpr bool Is64BitAddressSpace() const { return this->kip_header->Is64BitAddressSpace(); }
constexpr bool UsesSecureMemory() const { return this->kip_header->UsesSecureMemory(); }
bool Attach(u8 *bin) {
if (KInitialProcessHeader *header = reinterpret_cast<KInitialProcessHeader *>(bin); header->IsValid()) {
this->kip_header = header;
return true;
} else {
return false;
}
}
Result MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const;
Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter &params) const;
Result SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter &params) const;
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_readable_event.hpp>
#include <mesosphere/kern_k_interrupt_task.hpp>
namespace ams::kern {
class KInterruptEventTask;
class KInterruptEvent final : public KAutoObjectWithSlabHeapAndContainer<KInterruptEvent, KReadableEvent> {
MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent);
public:
/* TODO: This is a placeholder definition. */
};
class KInterruptEventTask : public KSlabAllocated<KInterruptEventTask>, public KInterruptTask {
public:
/* TODO: This is a placeholder definition. */
};
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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
namespace ams::kern {
class KInterruptTask;
class KInterruptHandler {
public:
virtual KInterruptTask *OnInterrupt(s32 interrupt_id) = 0;
};
class KInterruptTask : public KInterruptHandler {
private:
KInterruptTask *next_task;
public:
constexpr ALWAYS_INLINE KInterruptTask() : next_task(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE KInterruptTask *GetNextTask() const {
return this->next_task;
}
constexpr ALWAYS_INLINE void SetNextTask(KInterruptTask *t) {
this->next_task = t;
}
virtual void DoTask() = 0;
};
static ALWAYS_INLINE KInterruptTask *GetDummyInterruptTask() {
return reinterpret_cast<KInterruptTask *>(1);
}
}

View File

@@ -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 <mesosphere/kern_k_interrupt_task.hpp>
namespace ams::kern {
class KThread;
class KInterruptTaskManager {
private:
class TaskQueue {
private:
KInterruptTask *head;
KInterruptTask *tail;
public:
constexpr TaskQueue() : head(nullptr), tail(nullptr) { /* ... */ }
constexpr KInterruptTask *GetHead() { return this->head; }
constexpr bool IsEmpty() const { return this->head == nullptr; }
constexpr void Clear() { this->head = nullptr; this->tail = nullptr; }
void Enqueue(KInterruptTask *task);
void Dequeue();
};
private:
TaskQueue task_queue;
KThread *thread;
private:
static void ThreadFunction(uintptr_t arg);
void ThreadFunctionImpl();
public:
constexpr KInterruptTaskManager() : task_queue(), thread(nullptr) { /* ... */ }
constexpr KThread *GetThread() const { return this->thread; }
NOINLINE void Initialize();
void EnqueueTask(KInterruptTask *task);
/* TODO: Actually implement KInterruptTaskManager. This is a placeholder. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
namespace ams::kern {
class KLightSession;
class KLightClientSession final : public KAutoObjectWithSlabHeapAndContainer<KLightClientSession, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
private:
KLightSession *parent;
public:
constexpr KLightClientSession() : parent() { /* ... */ }
virtual ~KLightClientSession() { /* ... */ }
void Initialize(KLightSession *parent) {
/* Set member variables. */
this->parent = parent;
}
static void PostDestroy(uintptr_t arg) { /* ... */ }
constexpr const KLightSession *GetParent() const { return this->parent; }
/* TODO: More of KLightClientSession. */
};
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
#include <mesosphere/kern_k_thread_queue.hpp>
#include <mesosphere/kern_k_scoped_scheduler_lock_and_sleep.hpp>
namespace ams::kern {
class KLightConditionVariable {
private:
KThreadQueue thread_queue;
public:
constexpr ALWAYS_INLINE KLightConditionVariable() : thread_queue() { /* ... */ }
private:
void WaitImpl(KLightLock *lock, s64 timeout) {
KThread *owner = GetCurrentThreadPointer();
KHardwareTimer *timer;
/* Sleep the thread. */
{
KScopedSchedulerLockAndSleep lk(&timer, owner, timeout);
lock->Unlock();
if (!this->thread_queue.SleepThread(owner)) {
lk.CancelSleep();
return;
}
}
/* Cancel the task that the sleep setup. */
if (timer != nullptr) {
timer->CancelTask(owner);
}
}
public:
void Wait(KLightLock *lock, s64 timeout = -1ll) {
this->WaitImpl(lock, timeout);
lock->Lock();
}
void Broadcast() {
KScopedSchedulerLock lk;
while (this->thread_queue.WakeupFrontThread() != nullptr) {
/* We want to signal all threads, and so should continue waking up until there's nothing to wake. */
}
}
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_current_context.hpp>
#include <mesosphere/kern_k_scoped_lock.hpp>
namespace ams::kern {
class KLightLock {
private:
std::atomic<uintptr_t> tag;
public:
constexpr KLightLock() : tag(0) { /* ... */ }
void Lock() {
MESOSPHERE_ASSERT_THIS();
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer());
while (true) {
uintptr_t old_tag = this->tag.load(std::memory_order_relaxed);
while (!this->tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, std::memory_order_acquire)) {
/* ... */
}
if ((old_tag == 0) || ((old_tag | 1) == (cur_thread | 1))) {
break;
}
this->LockSlowPath(old_tag | 1, cur_thread);
}
}
void Unlock() {
MESOSPHERE_ASSERT_THIS();
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer());
uintptr_t expected = cur_thread;
if (!this->tag.compare_exchange_weak(expected, 0, std::memory_order_release)) {
this->UnlockSlowPath(cur_thread);
}
}
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
void UnlockSlowPath(uintptr_t cur_thread);
bool IsLocked() const { return this->tag != 0; }
bool IsLockedByCurrentThread() const { return (this->tag | 0x1ul) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer()) | 0x1ul); }
};
using KScopedLightLock = KScopedLock<KLightLock>;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_thread.hpp>
#include <mesosphere/kern_k_thread_queue.hpp>
namespace ams::kern {
class KLightSession;
class KLightServerSession final : public KAutoObjectWithSlabHeapAndContainer<KLightServerSession, KAutoObjectWithList>, public util::IntrusiveListBaseNode<KLightServerSession> {
MESOSPHERE_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
private:
KLightSession *parent;
KThreadQueue request_queue;
KThreadQueue server_queue;
KThread *current_request;
KThread *server_thread;
public:
constexpr KLightServerSession() : parent(), request_queue(), server_queue(), current_request(), server_thread() { /* ... */ }
virtual ~KLightServerSession() { /* ... */ }
void Initialize(KLightSession *parent);
static void PostDestroy(uintptr_t arg) { /* ... */ }
constexpr const KLightSession *GetParent() const { return this->parent; }
/* TODO: More of KLightServerSession. */
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_light_server_session.hpp>
#include <mesosphere/kern_k_light_client_session.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KClientPort;
class KProcess;
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
private:
KLightServerSession server;
KLightClientSession client;
State state;
KClientPort *port;
uintptr_t name;
KProcess *process;
bool initialized;
public:
constexpr KLightSession()
: server(), client(), state(State::Invalid), port(), name(), process(), initialized()
{
/* ... */
}
virtual ~KLightSession() { /* ... */ }
virtual bool IsInitialized() const override { return this->initialized; }
virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->process); }
static void PostDestroy(uintptr_t arg);
/* TODO: This is a placeholder definition. */
KLightClientSession &GetClientSession() { return this->client; }
KLightServerSession &GetServerSession() { return this->server; }
const KLightClientSession &GetClientSession() const { return this->client; }
const KLightServerSession &GetServerSession() const { return this->server; }
};
}

View File

@@ -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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KLinkedListNode : public util::IntrusiveListBaseNode<KLinkedListNode>, public KSlabAllocated<KLinkedListNode> {
private:
void *item;
public:
constexpr KLinkedListNode() : util::IntrusiveListBaseNode<KLinkedListNode>(), item(nullptr) { MESOSPHERE_ASSERT_THIS(); }
constexpr void Initialize(void *it) {
MESOSPHERE_ASSERT_THIS();
this->item = it;
}
constexpr void *GetItem() const {
return this->item;
}
};
static_assert(sizeof(KLinkedListNode) == sizeof(util::IntrusiveListNode) + sizeof(void *));
template<typename T>
class KLinkedList : private util::IntrusiveListBaseTraits<KLinkedListNode>::ListType {
private:
using BaseList = util::IntrusiveListBaseTraits<KLinkedListNode>::ListType;
public:
template<bool Const>
class Iterator;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = value_type *;
using const_pointer = const value_type *;
using reference = value_type &;
using const_reference = const value_type &;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
template<bool Const>
class Iterator {
private:
using BaseIterator = BaseList::Iterator<Const>;
friend class KLinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename KLinkedList::value_type;
using difference_type = typename KLinkedList::difference_type;
using pointer = typename std::conditional<Const, KLinkedList::const_pointer, KLinkedList::pointer>::type;
using reference = typename std::conditional<Const, KLinkedList::const_reference, KLinkedList::reference>::type;
private:
BaseIterator base_it;
public:
explicit Iterator(BaseIterator it) : base_it(it) { /* ... */ }
pointer GetItem() const {
return static_cast<pointer>(this->base_it->GetItem());
}
bool operator==(const Iterator &rhs) const {
return this->base_it == rhs.base_it;
}
bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->GetItem();
}
reference operator*() const {
return *this->GetItem();
}
Iterator &operator++() {
++this->base_it;
return *this;
}
Iterator &operator--() {
--this->base_it;
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(this->base_it);
}
};
public:
constexpr KLinkedList() : BaseList() { /* ... */ }
/* Iterator accessors. */
iterator begin() {
return iterator(BaseList::begin());
}
const_iterator begin() const {
return const_iterator(BaseList::begin());
}
iterator end() {
return iterator(BaseList::end());
}
const_iterator end() const {
return const_iterator(BaseList::end());
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
reverse_iterator rbegin() {
return reverse_iterator(this->end());
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(this->end());
}
reverse_iterator rend() {
return reverse_iterator(this->begin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(this->begin());
}
const_reverse_iterator crbegin() const {
return this->rbegin();
}
const_reverse_iterator crend() const {
return this->rend();
}
/* Content management. */
using BaseList::empty;
using BaseList::size;
reference back() {
return *(--this->end());
}
const_reference back() const {
return *(--this->end());
}
reference front() {
return *this->begin();
}
const_reference front() const {
return *this->begin();
}
iterator insert(const_iterator pos, reference ref) {
KLinkedListNode *node = KLinkedListNode::Allocate();
MESOSPHERE_ABORT_UNLESS(node != nullptr);
node->Initialize(std::addressof(ref));
return iterator(BaseList::insert(pos.base_it, *node));
}
void push_back(reference ref) {
this->insert(this->end(), ref);
}
void push_front(reference ref) {
this->insert(this->begin(), ref);
}
void pop_back() {
this->erase(--this->end());
}
void pop_front() {
this->erase(this->begin());
}
iterator erase(const iterator pos) {
KLinkedListNode *freed_node = std::addressof(*pos.base_it);
iterator ret = iterator(BaseList::erase(pos.base_it));
KLinkedListNode::Free(freed_node);
return ret;
}
};
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
enum KMemoryState : u32 {
KMemoryState_None = 0,
KMemoryState_Mask = 0xFF,
KMemoryState_All = ~KMemoryState_None,
KMemoryState_FlagCanReprotect = (1 << 8),
KMemoryState_FlagCanDebug = (1 << 9),
KMemoryState_FlagCanUseIpc = (1 << 10),
KMemoryState_FlagCanUseNonDeviceIpc = (1 << 11),
KMemoryState_FlagCanUseNonSecureIpc = (1 << 12),
KMemoryState_FlagMapped = (1 << 13),
KMemoryState_FlagCode = (1 << 14),
KMemoryState_FlagCanAlias = (1 << 15),
KMemoryState_FlagCanCodeAlias = (1 << 16),
KMemoryState_FlagCanTransfer = (1 << 17),
KMemoryState_FlagCanQueryPhysical = (1 << 18),
KMemoryState_FlagCanDeviceMap = (1 << 19),
KMemoryState_FlagCanAlignedDeviceMap = (1 << 20),
KMemoryState_FlagCanIpcUserBuffer = (1 << 21),
KMemoryState_FlagReferenceCounted = (1 << 22),
KMemoryState_FlagCanMapProcess = (1 << 23),
KMemoryState_FlagCanChangeAttribute = (1 << 24),
KMemoryState_FlagCanCodeMemory = (1 << 25),
KMemoryState_FlagsData = KMemoryState_FlagCanReprotect | KMemoryState_FlagCanUseIpc |
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
KMemoryState_FlagMapped | KMemoryState_FlagCanAlias |
KMemoryState_FlagCanTransfer | KMemoryState_FlagCanQueryPhysical |
KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap |
KMemoryState_FlagCanIpcUserBuffer | KMemoryState_FlagReferenceCounted |
KMemoryState_FlagCanChangeAttribute,
KMemoryState_FlagsCode = KMemoryState_FlagCanDebug | KMemoryState_FlagCanUseIpc |
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
KMemoryState_FlagMapped | KMemoryState_FlagCode |
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap |
KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagReferenceCounted,
KMemoryState_FlagsMisc = KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted |
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap,
KMemoryState_Free = ams::svc::MemoryState_Free,
KMemoryState_Io = ams::svc::MemoryState_Io | KMemoryState_FlagMapped,
KMemoryState_Static = ams::svc::MemoryState_Static | KMemoryState_FlagMapped | KMemoryState_FlagCanQueryPhysical,
KMemoryState_Code = ams::svc::MemoryState_Code | KMemoryState_FlagsCode | KMemoryState_FlagCanMapProcess,
KMemoryState_CodeData = ams::svc::MemoryState_CodeData | KMemoryState_FlagsData | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeMemory,
KMemoryState_Normal = ams::svc::MemoryState_Normal | KMemoryState_FlagsData | KMemoryState_FlagCanCodeMemory,
KMemoryState_Shared = ams::svc::MemoryState_Shared | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
/* KMemoryState_Alias was removed after 1.0.0. */
KMemoryState_AliasCode = ams::svc::MemoryState_AliasCode | KMemoryState_FlagsCode | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeAlias,
KMemoryState_AliasCodeData = ams::svc::MemoryState_AliasCodeData | KMemoryState_FlagsData | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeAlias | KMemoryState_FlagCanCodeMemory,
KMemoryState_Ipc = ams::svc::MemoryState_Ipc | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Stack = ams::svc::MemoryState_Stack | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_ThreadLocal = ams::svc::MemoryState_ThreadLocal | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
KMemoryState_Transfered = ams::svc::MemoryState_Transfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanChangeAttribute
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_SharedTransfered = ams::svc::MemoryState_SharedTransfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_SharedCode = ams::svc::MemoryState_SharedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Inaccessible = ams::svc::MemoryState_Inaccessible,
KMemoryState_NonSecureIpc = ams::svc::MemoryState_NonSecureIpc | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_NonDeviceIpc = ams::svc::MemoryState_NonDeviceIpc | KMemoryState_FlagsMisc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Kernel = ams::svc::MemoryState_Kernel | KMemoryState_FlagMapped,
KMemoryState_GeneratedCode = ams::svc::MemoryState_GeneratedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDebug,
KMemoryState_CodeOut = ams::svc::MemoryState_CodeOut | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
};
#if 1
static_assert(KMemoryState_Free == 0x00000000);
static_assert(KMemoryState_Io == 0x00002001);
static_assert(KMemoryState_Static == 0x00042002);
static_assert(KMemoryState_Code == 0x00DC7E03);
static_assert(KMemoryState_CodeData == 0x03FEBD04);
static_assert(KMemoryState_Normal == 0x037EBD05);
static_assert(KMemoryState_Shared == 0x00402006);
static_assert(KMemoryState_AliasCode == 0x00DD7E08);
static_assert(KMemoryState_AliasCodeData == 0x03FFBD09);
static_assert(KMemoryState_Ipc == 0x005C3C0A);
static_assert(KMemoryState_Stack == 0x005C3C0B);
static_assert(KMemoryState_ThreadLocal == 0x0040200C);
static_assert(KMemoryState_Transfered == 0x015C3C0D);
static_assert(KMemoryState_SharedTransfered == 0x005C380E);
static_assert(KMemoryState_SharedCode == 0x0040380F);
static_assert(KMemoryState_Inaccessible == 0x00000010);
static_assert(KMemoryState_NonSecureIpc == 0x005C3811);
static_assert(KMemoryState_NonDeviceIpc == 0x004C2812);
static_assert(KMemoryState_Kernel == 0x00002013);
static_assert(KMemoryState_GeneratedCode == 0x00402214);
static_assert(KMemoryState_CodeOut == 0x00402015);
#endif
enum KMemoryPermission : u8 {
KMemoryPermission_None = 0,
KMemoryPermission_All = static_cast<u8>(~KMemoryPermission_None),
KMemoryPermission_KernelShift = 3,
KMemoryPermission_KernelRead = ams::svc::MemoryPermission_Read << KMemoryPermission_KernelShift,
KMemoryPermission_KernelWrite = ams::svc::MemoryPermission_Write << KMemoryPermission_KernelShift,
KMemoryPermission_KernelExecute = ams::svc::MemoryPermission_Execute << KMemoryPermission_KernelShift,
KMemoryPermission_KernelReadWrite = KMemoryPermission_KernelRead | KMemoryPermission_KernelWrite,
KMemoryPermission_KernelReadExecute = KMemoryPermission_KernelRead | KMemoryPermission_KernelExecute,
KMemoryPermission_UserRead = ams::svc::MemoryPermission_Read | KMemoryPermission_KernelRead,
KMemoryPermission_UserWrite = ams::svc::MemoryPermission_Write | KMemoryPermission_KernelWrite,
KMemoryPermission_UserExecute = ams::svc::MemoryPermission_Execute,
KMemoryPermission_UserReadWrite = KMemoryPermission_UserRead | KMemoryPermission_UserWrite,
KMemoryPermission_UserReadExecute = KMemoryPermission_UserRead | KMemoryPermission_UserExecute,
KMemoryPermission_UserMask = ams::svc::MemoryPermission_Read | ams::svc::MemoryPermission_Write | ams::svc::MemoryPermission_Execute,
};
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
return static_cast<KMemoryPermission>((perm & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((perm & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift));
}
enum KMemoryAttribute : u8 {
KMemoryAttribute_None = 0x00,
KMemoryAttribute_Mask = 0x7F,
KMemoryAttribute_All = KMemoryAttribute_Mask,
KMemoryAttribute_DontCareMask = 0x80,
KMemoryAttribute_Locked = ams::svc::MemoryAttribute_Locked,
KMemoryAttribute_IpcLocked = ams::svc::MemoryAttribute_IpcLocked,
KMemoryAttribute_DeviceShared = ams::svc::MemoryAttribute_DeviceShared,
KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached,
};
static_assert((KMemoryAttribute_Mask & KMemoryAttribute_DontCareMask) == 0);
static_assert(static_cast<typename std::underlying_type<KMemoryAttribute>::type>(~(KMemoryAttribute_Mask | KMemoryAttribute_DontCareMask)) == 0);
struct KMemoryInfo {
uintptr_t address;
size_t size;
KMemoryState state;
KMemoryPermission perm;
KMemoryAttribute attribute;
KMemoryPermission original_perm;
u16 ipc_lock_count;
u16 device_use_count;
constexpr ams::svc::MemoryInfo GetSvcMemoryInfo() const {
return {
.addr = this->address,
.size = this->size,
.state = static_cast<ams::svc::MemoryState>(this->state & KMemoryState_Mask),
.attr = static_cast<ams::svc::MemoryAttribute>(this->attribute & KMemoryAttribute_Mask),
.perm = static_cast<ams::svc::MemoryPermission>(this->perm & KMemoryPermission_UserMask),
.ipc_refcount = this->ipc_lock_count,
.device_refcount = this->device_use_count,
};
}
constexpr uintptr_t GetAddress() const {
return this->address;
}
constexpr size_t GetSize() const {
return this->size;
}
constexpr size_t GetNumPages() const {
return this->GetSize() / PageSize;
}
constexpr uintptr_t GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr uintptr_t GetLastAddress() const {
return this->GetEndAddress() - 1;
}
};
class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
private:
KProcessAddress address;
size_t num_pages;
KMemoryState memory_state;
u16 ipc_lock_count;
u16 device_use_count;
KMemoryPermission perm;
KMemoryPermission original_perm;
KMemoryAttribute attribute;
public:
static constexpr ALWAYS_INLINE int Compare(const KMemoryBlock &lhs, const KMemoryBlock &rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
return 0;
} else {
return 1;
}
}
public:
constexpr KProcessAddress GetAddress() const {
return this->address;
}
constexpr size_t GetNumPages() const {
return this->num_pages;
}
constexpr size_t GetSize() const {
return this->GetNumPages() * PageSize;
}
constexpr KProcessAddress GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr KProcessAddress GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr KMemoryInfo GetMemoryInfo() const {
return {
.address = GetInteger(this->GetAddress()),
.size = this->GetSize(),
.state = this->memory_state,
.perm = this->perm,
.attribute = this->attribute,
.original_perm = this->original_perm,
.ipc_lock_count = this->ipc_lock_count,
.device_use_count = this->device_use_count,
};
}
public:
constexpr KMemoryBlock()
: address(), num_pages(), memory_state(KMemoryState_None), ipc_lock_count(), device_use_count(), perm(), original_perm(), attribute()
{
/* ... */
}
constexpr KMemoryBlock(KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr)
: address(addr), num_pages(np), memory_state(ms), ipc_lock_count(0), device_use_count(0), perm(p), original_perm(KMemoryPermission_None), attribute(attr)
{
/* ... */
}
constexpr void Initialize(KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) {
MESOSPHERE_ASSERT_THIS();
this->address = addr;
this->num_pages = np;
this->memory_state = ms;
this->ipc_lock_count = 0;
this->device_use_count = 0;
this->perm = p;
this->original_perm = KMemoryPermission_None;
this->attribute = attr;
}
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
MESOSPHERE_ASSERT_THIS();
constexpr auto AttributeIgnoreMask = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
return this->memory_state == s && this->perm == p && (this->attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
}
constexpr bool HasSameProperties(const KMemoryBlock &rhs) const {
MESOSPHERE_ASSERT_THIS();
return this->memory_state == rhs.memory_state &&
this->perm == rhs.perm &&
this->original_perm == rhs.original_perm &&
this->attribute == rhs.attribute &&
this->ipc_lock_count == rhs.ipc_lock_count &&
this->device_use_count == rhs.device_use_count;
}
constexpr bool Contains(KProcessAddress addr) const {
MESOSPHERE_ASSERT_THIS();
return this->GetAddress() <= addr && addr <= this->GetEndAddress();
}
constexpr void Add(size_t np) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(np > 0);
MESOSPHERE_ASSERT(this->GetAddress() + np * PageSize - 1 < this->GetEndAddress() + np * PageSize - 1);
this->num_pages += np;
}
constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->original_perm == KMemoryPermission_None);
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == 0);
this->memory_state = s;
this->perm = p;
this->attribute = static_cast<KMemoryAttribute>(a | (this->attribute & (KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared)));
}
constexpr void Split(KMemoryBlock *block, KProcessAddress addr) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->GetAddress() < addr);
MESOSPHERE_ASSERT(this->Contains(addr));
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), PageSize));
block->address = this->address;
block->num_pages = (addr - this->GetAddress()) / PageSize;
block->memory_state = this->memory_state;
block->ipc_lock_count = this->ipc_lock_count;
block->device_use_count = this->device_use_count;
block->perm = this->perm;
block->original_perm = this->original_perm;
block->attribute = this->attribute;
this->address = addr;
this->num_pages -= block->num_pages;
}
};
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_memory_block.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
namespace ams::kern {
class KMemoryBlockManagerUpdateAllocator {
public:
static constexpr size_t NumBlocks = 2;
private:
KMemoryBlock *blocks[NumBlocks];
size_t index;
KMemoryBlockSlabManager *slab_manager;
Result result;
public:
explicit KMemoryBlockManagerUpdateAllocator(KMemoryBlockSlabManager *sm) : blocks(), index(), slab_manager(sm), result(svc::ResultOutOfResource()) {
for (size_t i = 0; i < NumBlocks; i++) {
this->blocks[i] = this->slab_manager->Allocate();
if (this->blocks[i] == nullptr) {
this->result = svc::ResultOutOfResource();
return;
}
}
this->result = ResultSuccess();
}
~KMemoryBlockManagerUpdateAllocator() {
for (size_t i = 0; i < NumBlocks; i++) {
if (this->blocks[i] != nullptr) {
this->slab_manager->Free(this->blocks[i]);
}
}
}
Result GetResult() const {
return this->result;
}
KMemoryBlock *Allocate() {
MESOSPHERE_ABORT_UNLESS(this->index < NumBlocks);
MESOSPHERE_ABORT_UNLESS(this->blocks[this->index] != nullptr);
KMemoryBlock *block = nullptr;
std::swap(block, this->blocks[this->index++]);
return block;
}
void Free(KMemoryBlock *block) {
MESOSPHERE_ABORT_UNLESS(this->index <= NumBlocks);
MESOSPHERE_ABORT_UNLESS(block != nullptr);
if (this->index == 0) {
this->slab_manager->Free(block);
} else {
this->blocks[--this->index] = block;
}
}
};
class KMemoryBlockManager {
public:
using MemoryBlockTree = util::IntrusiveRedBlackTreeBaseTraits<KMemoryBlock>::TreeType<KMemoryBlock>;
using iterator = MemoryBlockTree::iterator;
using const_iterator = MemoryBlockTree::const_iterator;
private:
MemoryBlockTree memory_block_tree;
KProcessAddress start_address;
KProcessAddress end_address;
public:
constexpr KMemoryBlockManager() : memory_block_tree(), start_address(), end_address() { /* ... */ }
iterator end() { return this->memory_block_tree.end(); }
const_iterator end() const { return this->memory_block_tree.end(); }
const_iterator cend() const { return this->memory_block_tree.cend(); }
Result Initialize(KProcessAddress st, KProcessAddress nd, KMemoryBlockSlabManager *slab_manager);
void Finalize(KMemoryBlockSlabManager *slab_manager);
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
iterator FindIterator(KProcessAddress address) const {
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));
}
const KMemoryBlock *FindBlock(KProcessAddress address) const {
if (const_iterator it = this->FindIterator(address); it != this->memory_block_tree.end()) {
return std::addressof(*it);
}
return nullptr;
}
/* Debug. */
bool CheckState() const;
void DumpBlocks() const;
};
class KScopedMemoryBlockManagerAuditor {
private:
KMemoryBlockManager *manager;
public:
explicit ALWAYS_INLINE KScopedMemoryBlockManagerAuditor(KMemoryBlockManager *m) : manager(m) { MESOSPHERE_AUDIT(this->manager->CheckState()); }
explicit ALWAYS_INLINE KScopedMemoryBlockManagerAuditor(KMemoryBlockManager &m) : KScopedMemoryBlockManagerAuditor(std::addressof(m)) { /* ... */ }
ALWAYS_INLINE ~KScopedMemoryBlockManagerAuditor() { MESOSPHERE_AUDIT(this->manager->CheckState()); }
};
}

View File

@@ -0,0 +1,700 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/init/kern_init_page_table_select.hpp>
namespace ams::kern {
constexpr size_t KernelAslrAlignment = 2_MB;
constexpr size_t KernelVirtualAddressSpaceWidth = size_t(1ul) << 39ul;
constexpr size_t KernelPhysicalAddressSpaceWidth = size_t(1ul) << 48ul;
constexpr size_t KernelVirtualAddressSpaceBase = 0ul - KernelVirtualAddressSpaceWidth;
constexpr size_t KernelVirtualAddressSpaceEnd = KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
constexpr size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ul;
constexpr size_t KernelVirtualAddressSpaceSize = KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
constexpr size_t KernelPhysicalAddressSpaceBase = 0ul;
constexpr size_t KernelPhysicalAddressSpaceEnd = KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth;
constexpr size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ul;
constexpr size_t KernelPhysicalAddressSpaceSize = KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase;
enum KMemoryRegionType : u32 {
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
KMemoryRegionAttr_NoUserMap = 0x40000000,
KMemoryRegionAttr_LinearMapped = 0x80000000,
KMemoryRegionType_None = 0,
KMemoryRegionType_Kernel = 1,
KMemoryRegionType_Dram = 2,
KMemoryRegionType_CoreLocal = 4,
KMemoryRegionType_VirtualKernelPtHeap = 0x2A,
KMemoryRegionType_VirtualKernelTraceBuffer = 0x4A,
KMemoryRegionType_VirtualKernelInitPt = 0x19A,
KMemoryRegionType_VirtualDramMetadataPool = 0x29A,
KMemoryRegionType_VirtualDramManagedPool = 0x31A,
KMemoryRegionType_VirtualDramApplicationPool = 0x271A,
KMemoryRegionType_VirtualDramAppletPool = 0x1B1A,
KMemoryRegionType_VirtualDramSystemPool = 0x2B1A,
KMemoryRegionType_VirtualDramSystemNonSecurePool = 0x331A,
KMemoryRegionType_Uart = 0x1D,
KMemoryRegionType_InterruptDistributor = 0x4D | KMemoryRegionAttr_NoUserMap,
KMemoryRegionType_InterruptCpuInterface = 0x2D | KMemoryRegionAttr_NoUserMap,
KMemoryRegionType_MemoryController = 0x55,
KMemoryRegionType_MemoryController0 = 0x95,
KMemoryRegionType_MemoryController1 = 0x65,
KMemoryRegionType_PowerManagementController = 0x1A5,
KMemoryRegionType_KernelAutoMap = KMemoryRegionType_Kernel | KMemoryRegionAttr_ShouldKernelMap,
KMemoryRegionType_KernelTemp = 0x31,
KMemoryRegionType_KernelCode = 0x19,
KMemoryRegionType_KernelStack = 0x29,
KMemoryRegionType_KernelMisc = 0x49,
KMemoryRegionType_KernelSlab = 0x89,
KMemoryRegionType_KernelMiscMainStack = 0xB49,
KMemoryRegionType_KernelMiscMappedDevice = 0xD49,
KMemoryRegionType_KernelMiscIdleStack = 0x1349,
KMemoryRegionType_KernelMiscUnknownDebug = 0x1549,
KMemoryRegionType_KernelMiscExceptionStack = 0x2349,
KMemoryRegionType_DramLinearMapped = KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramReservedEarly = 0x16 | KMemoryRegionAttr_NoUserMap,
KMemoryRegionType_DramPoolPartition = 0x26 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramMetadataPool = 0x166 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected,
KMemoryRegionType_DramNonKernel = 0x1A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramApplicationPool = 0x7A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramAppletPool = 0xBA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramSystemNonSecurePool = 0xDA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramSystemPool = 0x13A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected,
KMemoryRegionType_DramKernel = 0xE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected,
KMemoryRegionType_DramKernelCode = 0xCE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected,
KMemoryRegionType_DramKernelSlab = 0x14E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected,
KMemoryRegionType_DramKernelPtHeap = 0x24E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_DramKernelInitPt = 0x44E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_LinearMapped,
/* These regions aren't normally mapped in retail kernel. */
KMemoryRegionType_KernelTraceBuffer = 0xA6 | KMemoryRegionAttr_UserReadOnly | KMemoryRegionAttr_LinearMapped,
KMemoryRegionType_OnMemoryBootImage = 0x156,
KMemoryRegionType_DTB = 0x256,
};
constexpr ALWAYS_INLINE KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
if (type_id == (type_id | KMemoryRegionType_KernelTraceBuffer)) {
return KMemoryRegionType_VirtualKernelTraceBuffer;
} else if (type_id == (type_id | KMemoryRegionType_DramKernelPtHeap)) {
return KMemoryRegionType_VirtualKernelPtHeap;
} else {
return KMemoryRegionType_Dram;
}
}
class KMemoryRegion : public util::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
NON_COPYABLE(KMemoryRegion);
NON_MOVEABLE(KMemoryRegion);
private:
uintptr_t address;
uintptr_t pair_address;
size_t region_size;
u32 attributes;
u32 type_id;
public:
static constexpr ALWAYS_INLINE int Compare(const KMemoryRegion &lhs, const KMemoryRegion &rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
return 0;
} else {
return 1;
}
}
public:
constexpr ALWAYS_INLINE KMemoryRegion() : address(0), pair_address(0), region_size(0), attributes(0), type_id(0) { /* ... */ }
constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, uintptr_t p, u32 r, u32 t) :
address(a), pair_address(p), region_size(rs), attributes(r), type_id(t)
{
/* ... */
}
constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, u32 r, u32 t) : KMemoryRegion(a, rs, std::numeric_limits<uintptr_t>::max(), r, t) { /* ... */ }
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
return this->address;
}
constexpr ALWAYS_INLINE uintptr_t GetPairAddress() const {
return this->pair_address;
}
constexpr ALWAYS_INLINE size_t GetSize() const {
return this->region_size;
}
constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr ALWAYS_INLINE u32 GetAttributes() const {
return this->attributes;
}
constexpr ALWAYS_INLINE u32 GetType() const {
return this->type_id;
}
constexpr ALWAYS_INLINE void SetType(u32 type) {
MESOSPHERE_INIT_ABORT_UNLESS(this->CanDerive(type));
this->type_id = type;
}
constexpr ALWAYS_INLINE bool Contains(uintptr_t address) const {
return this->GetAddress() <= address && address <= this->GetLastAddress();
}
constexpr ALWAYS_INLINE bool IsDerivedFrom(u32 type) const {
return (this->GetType() | type) == this->GetType();
}
constexpr ALWAYS_INLINE bool HasTypeAttribute(KMemoryRegionType attr) const {
return (this->GetType() | attr) == this->GetType();
}
constexpr ALWAYS_INLINE bool CanDerive(u32 type) const {
return (this->GetType() | type) == type;
}
constexpr ALWAYS_INLINE void SetPairAddress(uintptr_t a) {
this->pair_address = a;
}
constexpr ALWAYS_INLINE void SetTypeAttribute(KMemoryRegionType attr) {
this->type_id |= attr;
}
};
static_assert(std::is_trivially_destructible<KMemoryRegion>::value);
class KMemoryRegionTree {
public:
struct DerivedRegionExtents {
const KMemoryRegion *first_region;
const KMemoryRegion *last_region;
constexpr DerivedRegionExtents() : first_region(nullptr), last_region(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE uintptr_t GetAddress() const {
return this->first_region->GetAddress();
}
constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const {
return this->last_region->GetEndAddress();
}
constexpr ALWAYS_INLINE size_t GetSize() const {
return this->GetEndAddress() - this->GetAddress();
}
constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const {
return this->GetEndAddress() - 1;
}
};
private:
using TreeType = util::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
public:
using value_type = TreeType::value_type;
using size_type = TreeType::size_type;
using difference_type = TreeType::difference_type;
using pointer = TreeType::pointer;
using const_pointer = TreeType::const_pointer;
using reference = TreeType::reference;
using const_reference = TreeType::const_reference;
using iterator = TreeType::iterator;
using const_iterator = TreeType::const_iterator;
private:
TreeType tree;
public:
constexpr ALWAYS_INLINE KMemoryRegionTree() : tree() { /* ... */ }
public:
iterator FindContainingRegion(uintptr_t address) {
return this->find(KMemoryRegion(address, 1, 0, 0));
}
iterator FindFirstRegionByTypeAttr(u32 type_id, u32 attr = 0) {
for (auto it = this->begin(); it != this->end(); it++) {
if (it->GetType() == type_id && it->GetAttributes() == attr) {
return it;
}
}
MESOSPHERE_INIT_ABORT();
}
iterator FindFirstRegionByType(u32 type_id) {
for (auto it = this->begin(); it != this->end(); it++) {
if (it->GetType() == type_id) {
return it;
}
}
MESOSPHERE_INIT_ABORT();
}
iterator FindFirstDerivedRegion(u32 type_id) {
for (auto it = this->begin(); it != this->end(); it++) {
if (it->IsDerivedFrom(type_id)) {
return it;
}
}
MESOSPHERE_INIT_ABORT();
}
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
DerivedRegionExtents extents;
MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region == nullptr);
MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region == nullptr);
for (auto it = this->cbegin(); it != this->cend(); it++) {
if (it->IsDerivedFrom(type_id)) {
if (extents.first_region == nullptr) {
extents.first_region = std::addressof(*it);
}
extents.last_region = std::addressof(*it);
}
}
MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region != nullptr);
MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region != nullptr);
return extents;
}
public:
NOINLINE bool Insert(uintptr_t address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
NOINLINE KVirtualAddress GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id);
ALWAYS_INLINE KVirtualAddress GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id, size_t guard_size) {
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
}
public:
/* Iterator accessors. */
iterator begin() {
return this->tree.begin();
}
const_iterator begin() const {
return this->tree.begin();
}
iterator end() {
return this->tree.end();
}
const_iterator end() const {
return this->tree.end();
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return this->tree.iterator_to(ref);
}
const_iterator iterator_to(const_reference ref) const {
return this->tree.iterator_to(ref);
}
/* Content management. */
bool empty() const {
return this->tree.empty();
}
reference back() {
return this->tree.back();
}
const_reference back() const {
return this->tree.back();
}
reference front() {
return this->tree.front();
}
const_reference front() const {
return this->tree.front();
}
/* GCC over-eagerly inlines this operation. */
NOINLINE iterator insert(reference ref) {
return this->tree.insert(ref);
}
NOINLINE iterator erase(iterator it) {
return this->tree.erase(it);
}
iterator find(const_reference ref) const {
return this->tree.find(ref);
}
iterator nfind(const_reference ref) const {
return this->tree.nfind(ref);
}
};
class KMemoryRegionAllocator {
NON_COPYABLE(KMemoryRegionAllocator);
NON_MOVEABLE(KMemoryRegionAllocator);
public:
static constexpr size_t MaxMemoryRegions = 1000;
friend class KMemoryLayout;
private:
KMemoryRegion region_heap[MaxMemoryRegions];
size_t num_regions;
private:
constexpr ALWAYS_INLINE KMemoryRegionAllocator() : region_heap(), num_regions() { /* ... */ }
public:
ALWAYS_INLINE KMemoryRegion *Allocate() {
/* Ensure we stay within the bounds of our heap. */
MESOSPHERE_INIT_ABORT_UNLESS(this->num_regions < MaxMemoryRegions);
return &this->region_heap[this->num_regions++];
}
template<typename... Args>
ALWAYS_INLINE KMemoryRegion *Create(Args&&... args) {
KMemoryRegion *region = this->Allocate();
new (region) KMemoryRegion(std::forward<Args>(args)...);
return region;
}
};
class KMemoryLayout {
private:
static /* constinit */ inline uintptr_t s_linear_phys_to_virt_diff;
static /* constinit */ inline uintptr_t s_linear_virt_to_phys_diff;
static /* constinit */ inline KMemoryRegionAllocator s_region_allocator;
static /* constinit */ inline KMemoryRegionTree s_virtual_tree;
static /* constinit */ inline KMemoryRegionTree s_physical_tree;
static /* constinit */ inline KMemoryRegionTree s_virtual_linear_tree;
static /* constinit */ inline KMemoryRegionTree s_physical_linear_tree;
private:
static ALWAYS_INLINE auto GetVirtualLinearExtents(const KMemoryRegionTree::DerivedRegionExtents physical) {
return KMemoryRegion(GetInteger(GetLinearVirtualAddress(physical.GetAddress())), physical.GetSize(), 0, KMemoryRegionType_None);
}
public:
static ALWAYS_INLINE KMemoryRegionAllocator &GetMemoryRegionAllocator() { return s_region_allocator; }
static ALWAYS_INLINE KMemoryRegionTree &GetVirtualMemoryRegionTree() { return s_virtual_tree; }
static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalMemoryRegionTree() { return s_physical_tree; }
static ALWAYS_INLINE KMemoryRegionTree &GetVirtualLinearMemoryRegionTree() { return s_virtual_linear_tree; }
static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalLinearMemoryRegionTree() { return s_physical_linear_tree; }
static ALWAYS_INLINE KMemoryRegionTree::iterator GetEnd(KVirtualAddress) {
return GetVirtualLinearMemoryRegionTree().end();
}
static ALWAYS_INLINE KMemoryRegionTree::iterator GetEnd(KPhysicalAddress) {
return GetPhysicalMemoryRegionTree().end();
}
static NOINLINE KMemoryRegionTree::iterator FindContainingRegion(KVirtualAddress address) {
return GetVirtualMemoryRegionTree().FindContainingRegion(GetInteger(address));
}
static NOINLINE KMemoryRegionTree::iterator FindContainingRegion(KPhysicalAddress address) {
return GetPhysicalMemoryRegionTree().FindContainingRegion(GetInteger(address));
}
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress address) {
return GetInteger(address) + s_linear_phys_to_virt_diff;
}
static ALWAYS_INLINE KPhysicalAddress GetLinearPhysicalAddress(KVirtualAddress address) {
return GetInteger(address) + s_linear_virt_to_phys_diff;
}
static NOINLINE KVirtualAddress GetMainStackTopAddress(s32 core_id) {
return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscMainStack, static_cast<u32>(core_id))->GetEndAddress();
}
static NOINLINE KVirtualAddress GetIdleStackTopAddress(s32 core_id) {
return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscIdleStack, static_cast<u32>(core_id))->GetEndAddress();
}
static NOINLINE KVirtualAddress GetExceptionStackTopAddress(s32 core_id) {
return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscExceptionStack, static_cast<u32>(core_id))->GetEndAddress();
}
static NOINLINE KVirtualAddress GetSlabRegionAddress() {
return GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelSlab)->GetAddress();
}
static NOINLINE KVirtualAddress GetCoreLocalRegionAddress() {
return GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_CoreLocal)->GetAddress();
}
static NOINLINE KVirtualAddress GetInterruptDistributorAddress() {
return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_InterruptDistributor)->GetPairAddress();
}
static NOINLINE KVirtualAddress GetInterruptCpuInterfaceAddress() {
return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_InterruptCpuInterface)->GetPairAddress();
}
static NOINLINE KVirtualAddress GetUartAddress() {
return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_Uart)->GetPairAddress();
}
static NOINLINE KMemoryRegion &GetMemoryControllerRegion() {
return *GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_MemoryController);
}
static NOINLINE KMemoryRegion &GetMetadataPoolRegion() {
return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_VirtualDramMetadataPool);
}
static NOINLINE KMemoryRegion &GetPageTableHeapRegion() {
return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_VirtualKernelPtHeap);
}
static NOINLINE KMemoryRegion &GetKernelStackRegion() {
return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelStack);
}
static NOINLINE KMemoryRegion &GetTempRegion() {
return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelTemp);
}
static NOINLINE KMemoryRegion &GetVirtualLinearRegion(KVirtualAddress address) {
return *GetVirtualLinearMemoryRegionTree().FindContainingRegion(GetInteger(address));
}
static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) {
auto &tree = GetPhysicalLinearMemoryRegionTree();
KMemoryRegionTree::const_iterator it = tree.end();
if (hint != nullptr) {
it = tree.iterator_to(*hint);
}
if (it == tree.end() || !it->Contains(GetInteger(address))) {
it = tree.FindContainingRegion(GetInteger(address));
}
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) {
if (out) {
*out = std::addressof(*it);
}
return true;
}
return false;
}
static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, size_t size, const KMemoryRegion *hint = nullptr) {
auto &tree = GetPhysicalLinearMemoryRegionTree();
KMemoryRegionTree::const_iterator it = tree.end();
if (hint != nullptr) {
it = tree.iterator_to(*hint);
}
if (it == tree.end() || !it->Contains(GetInteger(address))) {
it = tree.FindContainingRegion(GetInteger(address));
}
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) {
const uintptr_t last_address = GetInteger(address) + size - 1;
do {
if (last_address <= it->GetLastAddress()) {
if (out) {
*out = std::addressof(*it);
}
return true;
}
it++;
} while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel));
}
return false;
}
static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion **out, KVirtualAddress address, const KMemoryRegion *hint = nullptr) {
auto &tree = GetVirtualLinearMemoryRegionTree();
KMemoryRegionTree::const_iterator it = tree.end();
if (hint != nullptr) {
it = tree.iterator_to(*hint);
}
if (it == tree.end() || !it->Contains(GetInteger(address))) {
it = tree.FindContainingRegion(GetInteger(address));
}
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)) {
if (out) {
*out = std::addressof(*it);
}
return true;
}
return false;
}
static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion **out, KVirtualAddress address, size_t size, const KMemoryRegion *hint = nullptr) {
auto &tree = GetVirtualLinearMemoryRegionTree();
KMemoryRegionTree::const_iterator it = tree.end();
if (hint != nullptr) {
it = tree.iterator_to(*hint);
}
if (it == tree.end() || !it->Contains(GetInteger(address))) {
it = tree.FindContainingRegion(GetInteger(address));
}
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)) {
const uintptr_t last_address = GetInteger(address) + size - 1;
do {
if (last_address <= it->GetLastAddress()) {
if (out) {
*out = std::addressof(*it);
}
return true;
}
it++;
} while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool));
}
return false;
}
static NOINLINE std::tuple<size_t, size_t> GetTotalAndKernelMemorySizes() {
size_t total_size = 0, kernel_size = 0;
for (auto it = GetPhysicalMemoryRegionTree().cbegin(); it != GetPhysicalMemoryRegionTree().cend(); it++) {
if (it->IsDerivedFrom(KMemoryRegionType_Dram)) {
total_size += it->GetSize();
if (!it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) {
kernel_size += it->GetSize();
}
}
}
return std::make_tuple(total_size, kernel_size);
}
static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start);
static NOINLINE auto GetKernelRegionExtents() {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
}
static NOINLINE auto GetKernelCodeRegionExtents() {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode);
}
static NOINLINE auto GetKernelStackRegionExtents() {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack);
}
static NOINLINE auto GetKernelMiscRegionExtents() {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc);
}
static NOINLINE auto GetKernelSlabRegionExtents() {
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab);
}
static NOINLINE const KMemoryRegion &GetCoreLocalRegion() {
return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_CoreLocal);
}
static NOINLINE auto GetLinearRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_LinearMapped);
}
static NOINLINE auto GetLinearRegionExtents() {
return GetVirtualLinearExtents(GetLinearRegionPhysicalExtents());
}
static NOINLINE auto GetCarveoutRegionExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_CarveoutProtected);
}
static NOINLINE auto GetKernelRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernel);
}
static NOINLINE auto GetKernelCodeRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelCode);
}
static NOINLINE auto GetKernelSlabRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelSlab);
}
static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap);
}
static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt);
}
static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition);
}
static NOINLINE auto GetKernelMetadataPoolRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramMetadataPool);
}
static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool);
}
static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool);
}
static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool);
}
static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() {
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool);
}
};
namespace init {
/* These should be generic, regardless of board. */
void SetupCoreLocalRegionMemoryRegions(KInitialPageTable &page_table, KInitialPageAllocator &page_allocator);
void SetupPoolPartitionMemoryRegions();
/* These may be implemented in a board-specific manner. */
void SetupDevicePhysicalMemoryRegions();
void SetupDramPhysicalMemoryRegions();
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
#include <mesosphere/kern_k_page_heap.hpp>
namespace ams::kern {
class KPageGroup;
class KMemoryManager {
public:
enum Pool {
Pool_Application = 0,
Pool_Applet = 1,
Pool_System = 2,
Pool_SystemNonSecure = 3,
Pool_Count,
Pool_Shift = 4,
Pool_Mask = (0xF << Pool_Shift),
};
enum Direction {
Direction_FromFront = 0,
Direction_FromBack = 1,
Direction_Shift = 0,
Direction_Mask = (0xF << Direction_Shift),
};
static constexpr size_t MaxManagerCount = 10;
private:
class Impl {
private:
using RefCount = u16;
private:
KPageHeap heap;
RefCount *page_reference_counts;
KVirtualAddress metadata_region;
Pool pool;
Impl *next;
Impl *prev;
public:
constexpr Impl() : heap(), page_reference_counts(), metadata_region(), pool(), next(), prev() { /* ... */ }
size_t Initialize(const KMemoryRegion *region, Pool pool, KVirtualAddress metadata_region, KVirtualAddress metadata_region_end);
KVirtualAddress AllocateBlock(s32 index) { return this->heap.AllocateBlock(index); }
void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); }
void TrackAllocationForOptimizedProcess(KVirtualAddress block, size_t num_pages);
constexpr size_t GetSize() const { return this->heap.GetSize(); }
constexpr KVirtualAddress GetEndAddress() const { return this->heap.GetEndAddress(); }
constexpr void SetNext(Impl *n) { this->next = n; }
constexpr void SetPrev(Impl *n) { this->prev = n; }
constexpr Impl *GetNext() const { return this->next; }
constexpr Impl *GetPrev() const { return this->prev; }
void Open(KLightLock *pool_locks, KVirtualAddress address, size_t num_pages) {
KScopedLightLock lk(pool_locks[this->pool]);
size_t index = this->heap.GetPageOffset(address);
const size_t end = index + num_pages;
while (index < end) {
const RefCount ref_count = (++this->page_reference_counts[index]);
MESOSPHERE_ABORT_UNLESS(ref_count > 0);
index++;
}
}
void Close(KLightLock *pool_locks, KVirtualAddress address, size_t num_pages) {
KScopedLightLock lk(pool_locks[this->pool]);
size_t index = this->heap.GetPageOffset(address);
const size_t end = index + num_pages;
size_t free_start = 0;
size_t free_count = 0;
while (index < end) {
MESOSPHERE_ABORT_UNLESS(this->page_reference_counts[index] > 0);
const RefCount ref_count = (--this->page_reference_counts[index]);
/* Keep track of how many zero refcounts we see in a row, to minimize calls to free. */
if (ref_count == 0) {
if (free_count > 0) {
free_count++;
} else {
free_start = index;
free_count = 1;
}
} else {
if (free_count > 0) {
this->Free(this->heap.GetAddress() + free_start * PageSize, free_count);
free_count = 0;
}
}
index++;
}
if (free_count > 0) {
this->Free(this->heap.GetAddress() + free_start * PageSize, free_count);
}
}
public:
static size_t CalculateMetadataOverheadSize(size_t region_size);
};
private:
KLightLock pool_locks[Pool_Count];
Impl *pool_managers_head[Pool_Count];
Impl *pool_managers_tail[Pool_Count];
Impl managers[MaxManagerCount];
size_t num_managers;
u64 optimized_process_ids[Pool_Count];
bool has_optimized_process[Pool_Count];
private:
Impl &GetManager(KVirtualAddress address) {
return this->managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()];
}
constexpr Impl *GetFirstManager(Pool pool, Direction dir) {
return dir == Direction_FromBack ? this->pool_managers_tail[pool] : this->pool_managers_head[pool];
}
constexpr Impl *GetNextManager(Impl *cur, Direction dir) {
if (dir == Direction_FromBack) {
return cur->GetPrev();
} else {
return cur->GetNext();
}
}
public:
constexpr KMemoryManager()
: pool_locks(), pool_managers_head(), pool_managers_tail(), managers(), num_managers(), optimized_process_ids(), has_optimized_process()
{
/* ... */
}
NOINLINE void Initialize(KVirtualAddress metadata_region, size_t metadata_region_size);
NOINLINE KVirtualAddress AllocateContinuous(size_t num_pages, size_t align_pages, u32 option);
NOINLINE Result Allocate(KPageGroup *out, size_t num_pages, u32 option);
void Open(KVirtualAddress address, size_t num_pages) {
/* Repeatedly open references until we've done so for all pages. */
while (num_pages) {
auto &manager = this->GetManager(address);
const size_t cur_pages = std::min(num_pages, (manager.GetEndAddress() - address) / PageSize);
manager.Open(this->pool_locks, address, cur_pages);
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
void Close(KVirtualAddress address, size_t num_pages) {
/* Repeatedly close references until we've done so for all pages. */
while (num_pages) {
auto &manager = this->GetManager(address);
const size_t cur_pages = std::min(num_pages, (manager.GetEndAddress() - address) / PageSize);
manager.Close(this->pool_locks, address, cur_pages);
num_pages -= cur_pages;
address += cur_pages * PageSize;
}
}
public:
static size_t CalculateMetadataOverheadSize(size_t region_size) {
return Impl::CalculateMetadataOverheadSize(region_size);
}
static constexpr ALWAYS_INLINE u32 EncodeOption(Pool pool, Direction dir) {
return (pool << Pool_Shift) | (dir << Direction_Shift);
}
static constexpr ALWAYS_INLINE Pool GetPool(u32 option) {
return static_cast<Pool>((option & Pool_Mask) >> Pool_Shift);
}
static constexpr ALWAYS_INLINE Direction GetDirection(u32 option) {
return static_cast<Direction>((option & Direction_Mask) >> Direction_Shift);
}
static constexpr ALWAYS_INLINE std::tuple<Pool, Direction> DecodeOption(u32 option) {
return std::make_tuple(GetPool(option), GetDirection(option));
}
};
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
public:
static constexpr size_t NameLengthMax = 12;
using List = util::IntrusiveListBaseTraits<KObjectName>::ListType;
private:
char name[NameLengthMax];
KAutoObject *object;
public:
constexpr KObjectName() : name(), object() { /* ... */ }
public:
static Result NewFromName(KAutoObject *obj, const char *name);
static Result Delete(KAutoObject *obj, const char *name);
static KScopedAutoObject<KAutoObject> Find(const char *name);
template<typename Derived>
static Result Delete(const char *name) {
/* Find the object. */
KScopedAutoObject obj = Find(name);
R_UNLESS(obj.IsNotNull(), svc::ResultNotFound());
/* Cast the object to the desired type. */
Derived *derived = obj->DynamicCast<Derived *>();
R_UNLESS(derived != nullptr, svc::ResultNotFound());
return Delete(obj.GetPointerUnsafe(), name);
}
private:
static KScopedAutoObject<KAutoObject> FindImpl(const char *name);
void Initialize(KAutoObject *obj, const char *name);
bool MatchesName(const char *name) const;
KAutoObject *GetObject() const { return this->object; }
};
}

View File

@@ -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 <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
namespace ams::kern {
class KPageBuffer : public KSlabAllocated<KPageBuffer> {
private:
alignas(PageSize) u8 buffer[PageSize];
public:
KPageBuffer() {
std::memset(buffer, 0, sizeof(buffer));
}
ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() const {
return KMemoryLayout::GetLinearPhysicalAddress(KVirtualAddress(this));
}
static ALWAYS_INLINE KPageBuffer *FromPhysicalAddress(KPhysicalAddress phys_addr) {
const KVirtualAddress virt_addr = KMemoryLayout::GetLinearVirtualAddress(phys_addr);
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(virt_addr), PageSize));
return GetPointer<KPageBuffer>(virt_addr);
}
};
static_assert(sizeof(KPageBuffer) == PageSize);
static_assert(alignof(KPageBuffer) == PageSize);
}

View File

@@ -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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KBlockInfoManager;
class KBlockInfo : public util::IntrusiveListBaseNode<KBlockInfo> {
private:
KVirtualAddress address;
size_t num_pages;
public:
constexpr KBlockInfo() : util::IntrusiveListBaseNode<KBlockInfo>(), address(), num_pages() { /* ... */ }
constexpr void Initialize(KVirtualAddress addr, size_t np) {
this->address = addr;
this->num_pages = np;
}
constexpr KVirtualAddress GetAddress() const { return this->address; }
constexpr size_t GetNumPages() const { return this->num_pages; }
constexpr size_t GetSize() const { return this->GetNumPages() * PageSize; }
constexpr KVirtualAddress GetEndAddress() const { return this->GetAddress() + this->GetSize(); }
constexpr KVirtualAddress GetLastAddress() const { return this->GetEndAddress() - 1; }
constexpr bool IsEquivalentTo(const KBlockInfo &rhs) const {
return this->address == rhs.address && this->num_pages == rhs.num_pages;
}
constexpr bool operator==(const KBlockInfo &rhs) const {
return this->IsEquivalentTo(rhs);
}
constexpr bool operator!=(const KBlockInfo &rhs) const {
return !(*this == rhs);
}
constexpr bool IsStrictlyBefore(KVirtualAddress addr) const {
const KVirtualAddress end = this->GetEndAddress();
if (this->address != Null<KVirtualAddress> && end == Null<KVirtualAddress>) {
return false;
}
return end < addr;
}
constexpr bool operator<(KVirtualAddress addr) const {
return this->IsStrictlyBefore(addr);
}
constexpr bool TryConcatenate(KVirtualAddress addr, size_t np) {
if (addr != Null<KVirtualAddress> && addr == this->GetEndAddress()) {
this->num_pages += np;
return true;
}
return false;
}
};
class KPageGroup {
public:
using BlockInfoList = util::IntrusiveListBaseTraits<KBlockInfo>::ListType;
using iterator = BlockInfoList::const_iterator;
private:
BlockInfoList block_list;
KBlockInfoManager *manager;
public:
explicit KPageGroup(KBlockInfoManager *m) : block_list(), manager(m) { /* ... */ }
~KPageGroup() { this->Finalize(); }
void Finalize();
iterator begin() const { return this->block_list.begin(); }
iterator end() const { return this->block_list.end(); }
bool empty() const { return this->block_list.empty(); }
Result AddBlock(KVirtualAddress addr, size_t num_pages);
void Open() const;
void Close() const;
size_t GetNumPages() const;
bool IsEquivalentTo(const KPageGroup &rhs) const;
bool operator==(const KPageGroup &rhs) const {
return this->IsEquivalentTo(rhs);
}
bool operator!=(const KPageGroup &rhs) const {
return !(*this == rhs);
}
};
class KScopedPageGroup {
private:
const KPageGroup *group;
public:
explicit ALWAYS_INLINE KScopedPageGroup(const KPageGroup *gp) : group(gp) { if (this->group) { this->group->Open(); } }
explicit ALWAYS_INLINE KScopedPageGroup(const KPageGroup &gp) : KScopedPageGroup(std::addressof(gp)) { /* ... */ }
ALWAYS_INLINE ~KScopedPageGroup() { if (this->group) { this->group->Close(); } }
ALWAYS_INLINE void CancelClose() {
this->group = nullptr;
}
};
}

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