Compare commits
206 Commits
0.8.7
...
mesosphere
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2efdee5cb8 | ||
|
|
240f455bc0 | ||
|
|
6642373795 | ||
|
|
1a924ad317 | ||
|
|
91662decf0 | ||
|
|
529024448e | ||
|
|
757aa30e74 | ||
|
|
fb4e0988b9 | ||
|
|
ad879ca327 | ||
|
|
efe7325af3 | ||
|
|
173bde2eca | ||
|
|
acf32f841c | ||
|
|
be3550d382 | ||
|
|
9c8f818c29 | ||
|
|
eb7e4153d1 | ||
|
|
86c43331eb | ||
|
|
baa34ddab5 | ||
|
|
d9c97983a3 | ||
|
|
b6bbc4f3e5 | ||
|
|
195da2e599 | ||
|
|
1b3b26c3af | ||
|
|
0fb40d1ef5 | ||
|
|
4238d2e97f | ||
|
|
7b726c3184 | ||
|
|
40b860c239 | ||
|
|
5222b429c5 | ||
|
|
2949d08eb2 | ||
|
|
6d898acc98 | ||
|
|
f72836d72c | ||
|
|
65b20d0685 | ||
|
|
d4241fd8ef | ||
|
|
504c74bc57 | ||
|
|
c47a9931d9 | ||
|
|
4a1021f220 | ||
|
|
ac6762bb6c | ||
|
|
e2d8316401 | ||
|
|
0a0c05481e | ||
|
|
cd1f74154d | ||
|
|
698fa9fcb0 | ||
|
|
a4419dfc41 | ||
|
|
600afa5f0f | ||
|
|
9b1fb0c6df | ||
|
|
08970a82ea | ||
|
|
a035be66bd | ||
|
|
9318ab10b2 | ||
|
|
dffb233423 | ||
|
|
e57203a7d4 | ||
|
|
7fde5fbe40 | ||
|
|
1684e1d35c | ||
|
|
edcd4cbc26 | ||
|
|
be17f1f494 | ||
|
|
672204c993 | ||
|
|
6166262b5c | ||
|
|
b492096aed | ||
|
|
cfeebbd1c9 | ||
|
|
4078c9a07d | ||
|
|
ed982877bd | ||
|
|
745fa84e5e | ||
|
|
50e307b4b7 | ||
|
|
4387da1ecc | ||
|
|
d6502c174a | ||
|
|
f38965d0bd | ||
|
|
c2cb94062a | ||
|
|
cfa050cc8f | ||
|
|
bbcb3757bd | ||
|
|
a33ace8996 | ||
|
|
766097d0b7 | ||
|
|
5f5a8567ce | ||
|
|
7dbe61c94c | ||
|
|
422dce6974 | ||
|
|
1d429261e9 | ||
|
|
6f25e92892 | ||
|
|
6cc29185d2 | ||
|
|
13a1566b4e | ||
|
|
bbb658a7e5 | ||
|
|
ad812c8125 | ||
|
|
a64fdce505 | ||
|
|
7e950c3bcc | ||
|
|
d0d7772f98 | ||
|
|
674175e1c6 | ||
|
|
eab2d05680 | ||
|
|
56d7473451 | ||
|
|
bfd4d41834 | ||
|
|
defee07625 | ||
|
|
2c3111f9c9 | ||
|
|
ca2c171482 | ||
|
|
cfcb1cd3a1 | ||
|
|
bb6cc6532b | ||
|
|
03a425a579 | ||
|
|
41f5b39f6b | ||
|
|
b5dd621250 | ||
|
|
27cde7da7a | ||
|
|
377b8dae9f | ||
|
|
78828427a4 | ||
|
|
fe28dac9d3 | ||
|
|
a85102c97c | ||
|
|
9b11f1cb19 | ||
|
|
cf6b9de370 | ||
|
|
80c380e61e | ||
|
|
967fa456ba | ||
|
|
6ae6c80c25 | ||
|
|
142df74694 | ||
|
|
00db1dc286 | ||
|
|
1456246f60 | ||
|
|
7581306109 | ||
|
|
a4ee4d20ad | ||
|
|
625ac5b357 | ||
|
|
3f75a92fd2 | ||
|
|
b5e91ff9a4 | ||
|
|
db47a0c041 | ||
|
|
5e7b33cabd | ||
|
|
3cc79f4e11 | ||
|
|
7c36a827da | ||
|
|
ea90325535 | ||
|
|
72377c2345 | ||
|
|
cccef3b85c | ||
|
|
e10c6a3217 | ||
|
|
4ef7b83e34 | ||
|
|
4ca53e2ef1 | ||
|
|
d9da531b41 | ||
|
|
93fb060fac | ||
|
|
fe0d41623c | ||
|
|
4ea6ce3156 | ||
|
|
520b5f6c59 | ||
|
|
505324f625 | ||
|
|
9319463a6e | ||
|
|
31c4c33042 | ||
|
|
55a8154691 | ||
|
|
453c05cf7c | ||
|
|
4c5c78858c | ||
|
|
967613a261 | ||
|
|
348345340d | ||
|
|
38159bdf9a | ||
|
|
e58948a42b | ||
|
|
9c53c0c0cc | ||
|
|
7c5dc61795 | ||
|
|
6034beb084 | ||
|
|
8c3dae846e | ||
|
|
dcc93ce60e | ||
|
|
7fef83885f | ||
|
|
3207c38a44 | ||
|
|
5952ebab54 | ||
|
|
ad41d010d3 | ||
|
|
21db90bae9 | ||
|
|
cf4f74c8f9 | ||
|
|
bf5a649928 | ||
|
|
c5fa4660c8 | ||
|
|
0d4a0348b5 | ||
|
|
51858e732a | ||
|
|
edcfbf4254 | ||
|
|
d984621150 | ||
|
|
4b8ebfa7c3 | ||
|
|
bc44e02aed | ||
|
|
85e8506fa8 | ||
|
|
5633444d5e | ||
|
|
99106076e6 | ||
|
|
0a194cb6a6 | ||
|
|
f4a8124dc3 | ||
|
|
9ea1a2a941 | ||
|
|
bfa84e27c1 | ||
|
|
14683405be | ||
|
|
ccbab35deb | ||
|
|
2dfa1c96d1 | ||
|
|
d44b91826d | ||
|
|
3a8f9114fc | ||
|
|
9858d6fc95 | ||
|
|
f6645387b0 | ||
|
|
df963967f5 | ||
|
|
8313669716 | ||
|
|
13c825a8bb | ||
|
|
30485f1df9 | ||
|
|
7945a921ca | ||
|
|
b09adb6a34 | ||
|
|
c3875796df | ||
|
|
a1d4caa7b4 | ||
|
|
9f972831cc | ||
|
|
f50bfaf7d7 | ||
|
|
bfd04cfe92 | ||
|
|
cb74bc6bb8 | ||
|
|
be4ca7eee5 | ||
|
|
7b24b43477 | ||
|
|
253afc90a4 | ||
|
|
a09c08994f | ||
|
|
13ded6bd1c | ||
|
|
4ba6d8b24c | ||
|
|
cb88fdfd62 | ||
|
|
d69fc060f4 | ||
|
|
e04fcfff6b | ||
|
|
79c52e2b91 | ||
|
|
4ac8f2745b | ||
|
|
6004b7479e | ||
|
|
ed86c44a49 | ||
|
|
5c9d0f05b1 | ||
|
|
87f7a6ebdc | ||
|
|
664e5e6b52 | ||
|
|
9691286d73 | ||
|
|
81895c8019 | ||
|
|
1a396235cd | ||
|
|
732a6159f7 | ||
|
|
a3389e25c9 | ||
|
|
908de31a0e | ||
|
|
4e5f033e41 | ||
|
|
ae90a9d7a6 | ||
|
|
a67d4064f0 | ||
|
|
d0659377e8 | ||
|
|
ac07971211 |
21
Makefile
21
Makefile
@@ -50,11 +50,13 @@ dist: all
|
||||
mkdir atmosphere-$(AMSVER)/atmosphere
|
||||
mkdir atmosphere-$(AMSVER)/sept
|
||||
mkdir atmosphere-$(AMSVER)/switch
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
|
||||
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
|
||||
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin
|
||||
cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin
|
||||
@@ -65,13 +67,16 @@ dist: all
|
||||
cp common/defaults/system_settings.ini atmosphere-$(AMSVER)/atmosphere/system_settings.ini
|
||||
cp -r common/defaults/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
|
||||
cp -r common/defaults/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
|
||||
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
|
||||
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp
|
||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp
|
||||
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/exefs.nsp
|
||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034/exefs.nsp
|
||||
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036/exefs.nsp
|
||||
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/exefs.nsp
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags
|
||||
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag
|
||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags
|
||||
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000037/flags/boot2.flag
|
||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
|
||||
rm -r atmosphere-$(AMSVER)
|
||||
mkdir out
|
||||
|
||||
10
README.md
10
README.md
@@ -19,6 +19,16 @@ Atmosphère consists of multiple components, each of which replaces/modifies a d
|
||||
* Stratosphère: Custom Sysmodule(s), both Rosalina style to extend the kernel/provide new features, and of the loader reimplementation style to hook important system actions
|
||||
* Troposphère: Application-level Horizon OS patches, used to implement desirable CFW features
|
||||
|
||||
Licensing
|
||||
=====
|
||||
|
||||
This software is licensed under the terms of the GPLv2, with exemptions for specific projects noted below.
|
||||
|
||||
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.
|
||||
|
||||
Credits
|
||||
=====
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ stage2_entrypoint = 0xF0000000
|
||||
; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future.
|
||||
debugmode = 1
|
||||
debugmode_user = 0
|
||||
; Note: Disabling usermode exception handlers will cause atmosphere to not fail gracefully under error conditions.
|
||||
; Support will not be provided to users who disable these. If you do not know what you are doing, leave them on.
|
||||
disable_user_exception_handlers = 0
|
||||
|
||||
[stratosphere]
|
||||
; To force-enable nogc, add nogc = 1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -4,8 +4,15 @@ upload_enabled = u8!0x0
|
||||
; Enable USB 3.0 superspeed for homebrew
|
||||
[usb]
|
||||
usb30_force_enabled = u8!0x0
|
||||
; Control whether RO should ease its validation of NROs.
|
||||
; (note: this is normally not necessary, and ips patches can be used.)
|
||||
[ro]
|
||||
ease_nro_restriction = u8!0x0
|
||||
; Atmosphere custom settings
|
||||
[atmosphere]
|
||||
; Reboot from fatal automatically after some number of milliseconds.
|
||||
; If field is not present or 0, fatal will wait indefinitely for user input.
|
||||
fatal_auto_reboot_interval = u64!0x0
|
||||
; Make the power menu's "reboot" button reboot to payload.
|
||||
; Set to "normal" for normal reboot, "rcm" for rcm reboot.
|
||||
power_menu_reboot_function = str!payload
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_600 6
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_620 7
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_700 8
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_800 9
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_700
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_800
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE_100
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_700
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_800
|
||||
|
||||
/* TODO: What should this be, for release? */
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 8
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 7
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 10
|
||||
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 7
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1
|
||||
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
# Changelog
|
||||
## 0.8.10
|
||||
+ A bug was fixed that could cause incorrect system memory allocation on 5.0.0.
|
||||
+ 5.0.0 should now correctly have an additional 12 MiB allocated for sysmodules.
|
||||
+ Atmosphère features which check button presses now consider all controllers, isntead of just P1.
|
||||
+ Support was added for configuring language/region on a per-game basis.
|
||||
+ This is managed by editing `atmosphere/titles/<title id>/config.ini` for the game.
|
||||
+ To edit title language, edit `override_config!override_language`.
|
||||
+ The languages supported are `ja`, `en-US`, `fr`, `de`, `it`, `es`, `zh-CN`, `ko`, `nl`, `pt`, `ru`, `zh-TW`, `en-GB`, `fr-CA`, `es-419`, `zh-Hans`, `zh-Hant`.
|
||||
+ To edit title region, edit `override_config!override_region`.
|
||||
+ The regions supported are `jpn`, `usa`, `eur`, `aus`, `chn`, `kor`, `twn`.
|
||||
+ Atmosphère now provides a reimplementation of the `boot` system module.
|
||||
+ `boot` is responsible for performing hardware initialization, showing the Nintendo logo, and repairing NAND on system update failure.
|
||||
+ Atmosphère's `boot` implementation preserves AutoRCM during NAND repair.
|
||||
+ NAND repair occurs when an unexpected shutdown or error happens during a system update.
|
||||
+ This fixes a final edge case where AutoRCM might be removed by HOS, which could cause a user to burn fuses.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.8.9
|
||||
+ A number of bugs were fixed, including:
|
||||
+ A data abort was fixed when mounting certain partitions on NAND.
|
||||
+ All Stratosphère system modules now only maintain a connection to `sm` when actively using it.
|
||||
+ This helps mitigate the scenario where sm hits the limit of 64 active connections and crashes.
|
||||
+ This sometimes caused crashes when custom non-Atmosphère sysmodules were active and the user played certain games (ex: Smash's Stage Builder).
|
||||
+ fatal now uses the 8.0.0 clkrst API, instead of silently failing to adjust clock rates on that firmware version.
|
||||
+ A wait loop is now performed when trying to get a session to `sm`, in the case where `sm:` is not yet registered.
|
||||
+ This fixes a race condition that could cause a failure to boot under certain circumstances.
|
||||
+ libstratosphere's handling of domain object closing has been improved.
|
||||
+ Previously, this code could cause crashes/extremely odd behavior (misinterpreting what object a service is) under certain circumstances.
|
||||
+ An optional automatic reboot timer was added to fatal.
|
||||
+ By setting the system setting `atmosphere!fatal_auto_reboot_interval` to a non-zero u64 value, fatal can be made to automatically reboot after a certain number of milliseconds.
|
||||
+ If the setting is zero or not present, fatal will wait for user input as usual.
|
||||
+ Atmosphère now provides a reimplementation of the `ro` system module.
|
||||
+ `ro` is responsible for loading dynamic libraries (NROs) on 3.0.0+.
|
||||
+ On 1.0.0-2.3.0, this is handled by `loader`.
|
||||
+ Atmosphere's `ro` provides this functionality (`ldr:ro`, `ro:dmnt`) on all firmware versions.
|
||||
+ An extension was implemented to provide support for applying IPS patches to NROs.
|
||||
+ All patches at paths like /atmosphere/nro_patches/<user-defined patch name>/<Hex Build-ID for NRO to patch>.ips will be applied, allowing for easy distribution of patches.
|
||||
+ Both the IPS and IPS32 formats are supported.
|
||||
+ Atmosphère now provides a reimplementation of the `spl` system module.
|
||||
+ `spl` (Secure Platform Services) is responsible for cryptographic operations, including all communications with the secure monitor (exosphère).
|
||||
+ In the future, this may be used to provide extensions to the API for interacting with exosphère from userland.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.8.8
|
||||
+ Support was added for firmware version 8.0.0.
|
||||
+ Custom exception handlers were added to stratosphere modules.
|
||||
+ If a crash happens in a core atmosphere module now, instead of silently failing a reboot will occur to log the information to the SD card.
|
||||
+ A bug was fixed in creport that caused games to hang when crashing under certain circumstances.
|
||||
+ A bug was fixed that prevented maintenance mode from booting on 7.0.0+.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.8.7
|
||||
+ A few bugs were fixed that could cause fatal to fail to show an error under certain circumstances.
|
||||
+ A bug was fixed that caused an error when launching certain games (e.g. Hellblade: Senua's Sacrifice).
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# Thermosphère
|
||||
Thermosphère is a hypervisor based implementation of emuNAND. An emuNAND is a copy of the firmware on the Switch’s internal memory (sysNAND), and is typically installed on an external SD Card.
|
||||
Thermosphère is a hypervisor based implementation of emuNAND.
|
||||
|
||||
An emuNAND operates completely independently of the sysNAND. This allows one to make or test various modifications and homebrew safely without needing to restore their NAND backup afterwards by testing things on the emuNAND, and switching back to the sysNAND when finished. In the case of past Nintendo systems such as the 3DS, an emuNAND could also be used to update your system to the latest firmware while keeping your sysNAND on a lower version, however this may be more difficult to do on the Switch due to Nintendo using efuse technology for major system updates.
|
||||
|
||||
Thermosphère is currently planned to be included in the 1.0 release of Atmosphère.
|
||||
Thermosphère is currently planned to be included in a future release of Atmosphère.
|
||||
|
||||
@@ -122,4 +122,4 @@ By default, the Stratosphere implementation of PM will raise any session limits
|
||||
|
||||
### Weak Service Verification
|
||||
|
||||
In system firmware versions before 3.0.1, if a process did not call the [Initialize](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(0)) command on its `sm:` session, normally used to inform sm of the process's identity, sm would assume that the process was a kernel internal process and skip any service registration or access checks. The Stratosphere implementation of sm reimplements this vulnerability, allowing homebrew processes to skip service registration and access checks.
|
||||
In system firmware versions before 3.0.1, if a process did not call the [Initialize](https://reswitched.github.io/SwIPC/ifaces.html#nn::sm::detail::IUserInterface(0)) command on its `sm:` session, normally used to inform sm of the process's identity, sm would assume that the process was a kernel internal process and skip any service registration or access checks. The Stratosphere implementation of sm does not implement this vulnerability, and initialization is required on all firmware versions.
|
||||
|
||||
@@ -100,6 +100,8 @@ uint64_t bootconfig_get_value_for_sysctr0(void) {
|
||||
}
|
||||
|
||||
uint64_t bootconfig_get_memory_arrangement(void) {
|
||||
/* TODO: This function has changed pretty significantly since we implemented it. */
|
||||
/* Not relevant for retail, but we'll probably want this to be accurate sooner or later. */
|
||||
if (bootconfig_is_debug_mode()) {
|
||||
if (fuse_get_dram_id() == 4) {
|
||||
if (LOADED_BOOTCONFIG->unsigned_config.data[0x23]) {
|
||||
|
||||
@@ -35,13 +35,14 @@
|
||||
#undef u8
|
||||
#undef u32
|
||||
|
||||
static bool g_battery_profile = false;
|
||||
static bool g_hiz_mode_enabled = false;
|
||||
static bool g_debugmode_override_user = false, g_debugmode_override_priv = false;
|
||||
static bool g_enable_usermode_exception_handlers = true;
|
||||
|
||||
uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) {
|
||||
switch (item) {
|
||||
case CONFIGITEM_BATTERYPROFILE:
|
||||
g_battery_profile = (value != 0);
|
||||
case CONFIGITEM_HIZMODE:
|
||||
g_hiz_mode_enabled = (value != 0);
|
||||
break;
|
||||
case CONFIGITEM_NEEDS_REBOOT:
|
||||
/* Force a reboot, if requested. */
|
||||
@@ -133,8 +134,12 @@ bool configitem_is_retail(void) {
|
||||
return is_retail != 0;
|
||||
}
|
||||
|
||||
bool configitem_should_profile_battery(void) {
|
||||
return g_battery_profile;
|
||||
bool configitem_is_hiz_mode_enabled(void) {
|
||||
return g_hiz_mode_enabled;
|
||||
}
|
||||
|
||||
void configitem_set_hiz_mode_enabled(bool enabled) {
|
||||
g_hiz_mode_enabled = enabled;
|
||||
}
|
||||
|
||||
bool configitem_is_debugmode_priv(void) {
|
||||
@@ -159,6 +164,10 @@ void configitem_set_debugmode_override(bool user, bool priv) {
|
||||
g_debugmode_override_priv = priv;
|
||||
}
|
||||
|
||||
void configitem_disable_usermode_exception_handlers(void) {
|
||||
g_enable_usermode_exception_handlers = false;
|
||||
}
|
||||
|
||||
uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue) {
|
||||
uint32_t result = 0;
|
||||
switch (item) {
|
||||
@@ -209,13 +218,15 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
|
||||
case CONFIGITEM_KERNELCONFIGURATION:
|
||||
{
|
||||
uint64_t config = bootconfig_get_kernel_configuration();
|
||||
/* Always enable usermode exception handlers. */
|
||||
config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS;
|
||||
/* Enable usermode exception handlers by default. */
|
||||
if (g_enable_usermode_exception_handlers) {
|
||||
config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS;
|
||||
}
|
||||
*p_outvalue = config;
|
||||
}
|
||||
break;
|
||||
case CONFIGITEM_BATTERYPROFILE:
|
||||
*p_outvalue = (int)g_battery_profile;
|
||||
case CONFIGITEM_HIZMODE:
|
||||
*p_outvalue = (int)g_hiz_mode_enabled;
|
||||
break;
|
||||
case CONFIGITEM_ISQUESTUNIT:
|
||||
/* Added on 3.0, used to determine whether console is a kiosk unit. */
|
||||
|
||||
@@ -33,7 +33,7 @@ typedef enum {
|
||||
CONFIGITEM_MEMORYARRANGE = 10,
|
||||
CONFIGITEM_ISDEBUGMODE = 11,
|
||||
CONFIGITEM_KERNELCONFIGURATION = 12,
|
||||
CONFIGITEM_BATTERYPROFILE = 13,
|
||||
CONFIGITEM_HIZMODE = 13,
|
||||
CONFIGITEM_ISQUESTUNIT = 14,
|
||||
CONFIGITEM_NEWHARDWARETYPE_5X = 15,
|
||||
CONFIGITEM_NEWKEYGENERATION_5X = 16,
|
||||
@@ -56,10 +56,12 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue);
|
||||
|
||||
bool configitem_is_recovery_boot(void);
|
||||
bool configitem_is_retail(void);
|
||||
bool configitem_should_profile_battery(void);
|
||||
bool configitem_is_hiz_mode_enabled(void);
|
||||
bool configitem_is_debugmode_priv(void);
|
||||
|
||||
void configitem_set_debugmode_override(bool user, bool priv);
|
||||
void configitem_disable_usermode_exception_handlers(void);
|
||||
void configitem_set_hiz_mode_enabled(bool enabled);
|
||||
|
||||
uint64_t configitem_get_hardware_type(void);
|
||||
|
||||
|
||||
@@ -75,3 +75,11 @@ unsigned int exosphere_should_override_debugmode_user(void) {
|
||||
|
||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
|
||||
}
|
||||
|
||||
unsigned int exosphere_should_disable_usermode_exception_handlers(void) {
|
||||
if (!g_has_loaded_config) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,11 @@
|
||||
/* Exosphere config in DRAM shares physical/virtual mapping. */
|
||||
#define MAILBOX_EXOSPHERE_CONFIG_PHYS MAILBOX_EXOSPHERE_CONFIG
|
||||
|
||||
#define EXOSPHERE_FLAGS_DEFAULT 0x00000000
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
@@ -52,6 +53,7 @@ unsigned int exosphere_get_target_firmware(void);
|
||||
unsigned int exosphere_should_perform_620_keygen(void);
|
||||
unsigned int exosphere_should_override_debugmode_priv(void);
|
||||
unsigned int exosphere_should_override_debugmode_user(void);
|
||||
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
|
||||
|
||||
static inline unsigned int exosphere_get_target_firmware_for_init(void) {
|
||||
const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic;
|
||||
|
||||
@@ -140,13 +140,19 @@ void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint6
|
||||
carveout->size_big_pages = (uint32_t)(size >> 17);
|
||||
carveout->client_access_0 = (BIT(CSR_PTCR) | BIT(CSR_DISPLAY0A) | BIT(CSR_DISPLAY0AB) | BIT(CSR_DISPLAY0B) | BIT(CSR_DISPLAY0BB) | BIT(CSR_DISPLAY0C) | BIT(CSR_DISPLAY0CB) | BIT(CSR_AFIR) | BIT(CSR_DISPLAYHC) | BIT(CSR_DISPLAYHCB) | BIT(CSR_HDAR) | BIT(CSR_HOST1XDMAR) | BIT(CSR_HOST1XR) | BIT(CSR_NVENCSRD) | BIT(CSR_PPCSAHBDMAR) | BIT(CSR_PPCSAHBSLVR));
|
||||
carveout->client_access_1 = (BIT(CSR_MPCORER) | BIT(CSW_NVENCSWR) | BIT(CSW_AFIW) | BIT(CSW_HDAW) | BIT(CSW_HOST1XW) | BIT(CSW_MPCOREW) | BIT(CSW_PPCSAHBDMAW) | BIT(CSW_PPCSAHBSLVW));
|
||||
carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW) | BIT(CSR_TSECSRD) | BIT(CSW_TSECSWR));
|
||||
carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR));
|
||||
carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR));
|
||||
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) {
|
||||
carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW));
|
||||
carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR));
|
||||
carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR) | BIT(CSR_TSECSRDB) | BIT(CSW_TSECSWRB));
|
||||
} else {
|
||||
carveout->client_access_2 = (BIT(CSR_XUSB_HOSTR) | BIT(CSW_XUSB_HOSTW) | BIT(CSR_XUSB_DEVR) | BIT(CSW_XUSB_DEVW) | BIT(CSR_TSECSRD) | BIT(CSW_TSECSWR));
|
||||
carveout->client_access_3 = (BIT(CSR_SDMMCRA) | BIT(CSR_SDMMCRAA) | BIT(CSR_SDMMCRAB) | BIT(CSW_SDMMCWA) | BIT(CSW_SDMMCWAA) | BIT(CSW_SDMMCWAB) | BIT(CSR_VICSRD) | BIT(CSW_VICSWR) | BIT(CSR_DISPLAYD) | BIT(CSR_NVDECSRD) | BIT(CSW_NVDECSWR) | BIT(CSR_APER) | BIT(CSW_APEW) | BIT(CSR_NVJPGSRD) | BIT(CSW_NVJPGSWR));
|
||||
carveout->client_access_4 = (BIT(CSR_SESRD) | BIT(CSW_SESWR));
|
||||
}
|
||||
carveout->client_force_internal_access_0 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSR_AVPCARM7R) : 0;
|
||||
carveout->client_force_internal_access_1 = ((exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) && (carveout_id == 4)) ? BIT(CSW_AVPCARM7W) : 0;
|
||||
carveout->client_force_internal_access_2 = 0;
|
||||
carveout->client_force_internal_access_3 = 0;
|
||||
carveout->client_force_internal_access_4 = 0;
|
||||
carveout->config = 0x8B;
|
||||
carveout->config = (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_800) ? 0x4CB : 0x8B;
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ static void setup_se(void) {
|
||||
master_kek_source_ind = MASTERKEY_REVISION_620 - MASTERKEY_REVISION_620;
|
||||
break;
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
master_kek_source_ind = MASTERKEY_REVISION_700_CURRENT - MASTERKEY_REVISION_620;
|
||||
break;
|
||||
default:
|
||||
@@ -179,6 +180,7 @@ static void setup_se(void) {
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_600:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_620:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
|
||||
break;
|
||||
}
|
||||
@@ -334,10 +336,15 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
|
||||
}
|
||||
|
||||
/* Ensure no overlap with later sections. */
|
||||
for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) {
|
||||
uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section];
|
||||
if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) {
|
||||
return false;
|
||||
if (metadata->section_sizes[section] != 0) {
|
||||
for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) {
|
||||
if (metadata->section_sizes[later_section] == 0) {
|
||||
continue;
|
||||
}
|
||||
uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section];
|
||||
if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +397,7 @@ static uint32_t decrypt_and_validate_header(package2_header_t *header) {
|
||||
}
|
||||
|
||||
/* Ensure we successfully decrypted the header. */
|
||||
if (mkey_rev > mkey_get_revision()) {
|
||||
if (mkey_rev > mkey_get_revision()) {
|
||||
panic(0xFAF00003);
|
||||
}
|
||||
} else if (!validate_package2_metadata(&header->metadata)) {
|
||||
@@ -487,6 +494,7 @@ static void copy_warmboot_bin_to_dram() {
|
||||
warmboot_src = (uint8_t *)0x4003D800;
|
||||
break;
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
warmboot_src = (uint8_t *)0x4003E000;
|
||||
break;
|
||||
}
|
||||
@@ -530,6 +538,9 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
||||
/* Load Exosphere-specific config. */
|
||||
exosphere_load_config();
|
||||
configitem_set_debugmode_override(exosphere_should_override_debugmode_user() != 0, exosphere_should_override_debugmode_priv() != 0);
|
||||
if (exosphere_should_disable_usermode_exception_handlers() != 0) {
|
||||
configitem_disable_usermode_exception_handlers();
|
||||
}
|
||||
|
||||
/* Setup the Security Engine. */
|
||||
setup_se();
|
||||
@@ -554,6 +565,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
|
||||
MAKE_REG32(PMC_BASE + 0x360) = 0xA8;
|
||||
break;
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
MAKE_REG32(PMC_BASE + 0x360) = 0x129;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@
|
||||
#undef u8
|
||||
#undef u32
|
||||
|
||||
static void configure_battery_hi_z_mode(void) {
|
||||
static void configure_battery_hiz_mode(void) {
|
||||
clkrst_reboot(CARDEVICE_I2C1);
|
||||
|
||||
if (configitem_should_profile_battery() && !i2c_query_ti_charger_bit_7()) {
|
||||
/* Profile the battery. */
|
||||
if (configitem_is_hiz_mode_enabled() && !i2c_query_ti_charger_bit_7()) {
|
||||
/* Configure HiZ mode. */
|
||||
i2c_set_ti_charger_bit_7();
|
||||
uint32_t start_time = get_time();
|
||||
bool should_wait = true;
|
||||
@@ -109,7 +109,7 @@ static void mitigate_jamais_vu(void) {
|
||||
}
|
||||
|
||||
/* Jamais Vu mitigation #3: Ensure all relevant DMA controllers are held in reset. */
|
||||
if ((CLK_RST_CONTROLLER_RST_DEVICES_H_0 & 0x4000004) != 0x4000004) {
|
||||
if ((CLK_RST_CONTROLLER_RST_DEVICES_H_0 & 0x4000006) != 0x4000006) {
|
||||
generic_panic();
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen
|
||||
}
|
||||
|
||||
/* Perform I2C comms with TI charger if required. */
|
||||
configure_battery_hi_z_mode();
|
||||
configure_battery_hiz_mode();
|
||||
|
||||
/* Enable LP0 Wake Event Detection. */
|
||||
enable_lp0_wake_events();
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
#include "sc7.h"
|
||||
#include "exocfg.h"
|
||||
|
||||
#define SMC_USER_HANDLERS 0x13
|
||||
#define SMC_PRIV_HANDLERS 0x9
|
||||
|
||||
#define SMC_AMS_HANDLERS 0x2
|
||||
|
||||
#define DEBUG_LOG_SMCS 0
|
||||
#define DEBUG_PANIC_ON_FAILURE 0
|
||||
|
||||
@@ -83,8 +78,12 @@ uint32_t smc_read_write_register(smc_args_t *args);
|
||||
/* Atmosphere SMC prototypes */
|
||||
uint32_t smc_ams_iram_copy(smc_args_t *args);
|
||||
|
||||
/* TODO: Provide a way to set this. It's 0 on non-recovery boot anyway... */
|
||||
static uint32_t g_smc_blacklist_mask = 0;
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint32_t blacklist_mask;
|
||||
uint32_t (*handler)(smc_args_t *args);
|
||||
} smc_table_entry_t;
|
||||
|
||||
@@ -93,45 +92,49 @@ typedef struct {
|
||||
uint32_t num_handlers;
|
||||
} smc_table_t;
|
||||
|
||||
static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
|
||||
{0, NULL},
|
||||
{0xC3000401, smc_set_config_user},
|
||||
{0xC3000002, smc_get_config_user},
|
||||
{0xC3000003, smc_check_status},
|
||||
{0xC3000404, smc_get_result},
|
||||
{0xC3000E05, smc_exp_mod},
|
||||
{0xC3000006, smc_get_random_bytes_for_user},
|
||||
{0xC3000007, smc_generate_aes_kek},
|
||||
{0xC3000008, smc_load_aes_key},
|
||||
{0xC3000009, smc_crypt_aes},
|
||||
{0xC300000A, smc_generate_specific_aes_key},
|
||||
{0xC300040B, smc_compute_cmac},
|
||||
{0xC300100C, smc_load_rsa_oaep_key},
|
||||
{0xC300100D, smc_decrypt_rsa_private_key},
|
||||
{0xC300100E, smc_load_secure_exp_mod_key},
|
||||
{0xC300060F, smc_secure_exp_mod},
|
||||
{0xC3000610, smc_unwrap_rsa_oaep_wrapped_titlekey},
|
||||
{0xC3000011, smc_load_titlekey},
|
||||
{0xC3000012, smc_unwrap_aes_wrapped_titlekey}
|
||||
static smc_table_entry_t g_smc_user_table[] = {
|
||||
{0, 4, NULL},
|
||||
{0xC3000401, 4, smc_set_config_user},
|
||||
{0xC3000002, 1, smc_get_config_user},
|
||||
{0xC3000003, 1, smc_check_status},
|
||||
{0xC3000404, 1, smc_get_result},
|
||||
{0xC3000E05, 4, smc_exp_mod},
|
||||
{0xC3000006, 1, smc_get_random_bytes_for_user},
|
||||
{0xC3000007, 1, smc_generate_aes_kek},
|
||||
{0xC3000008, 1, smc_load_aes_key},
|
||||
{0xC3000009, 1, smc_crypt_aes},
|
||||
{0xC300000A, 1, smc_generate_specific_aes_key},
|
||||
{0xC300040B, 1, smc_compute_cmac},
|
||||
{0xC300100C, 1, smc_load_rsa_oaep_key},
|
||||
{0xC300100D, 2, smc_decrypt_rsa_private_key},
|
||||
{0xC300100E, 4, smc_load_secure_exp_mod_key},
|
||||
{0xC300060F, 2, smc_secure_exp_mod},
|
||||
{0xC3000610, 4, smc_unwrap_rsa_oaep_wrapped_titlekey},
|
||||
{0xC3000011, 4, smc_load_titlekey},
|
||||
{0xC3000012, 4, smc_unwrap_aes_wrapped_titlekey}
|
||||
};
|
||||
#define SMC_USER_HANDLERS (sizeof(g_smc_user_table) / sizeof(g_smc_user_table[0]))
|
||||
|
||||
static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = {
|
||||
{0, NULL},
|
||||
{0xC4000001, smc_cpu_suspend},
|
||||
{0x84000002, smc_cpu_off},
|
||||
{0xC4000003, smc_cpu_on},
|
||||
{0xC3000004, smc_get_config_priv},
|
||||
{0xC3000005, smc_get_random_bytes_for_priv},
|
||||
{0xC3000006, smc_panic},
|
||||
{0xC3000007, smc_configure_carveout},
|
||||
{0xC3000008, smc_read_write_register}
|
||||
static smc_table_entry_t g_smc_priv_table[] = {
|
||||
{0, 4, NULL},
|
||||
{0xC4000001, 4, smc_cpu_suspend},
|
||||
{0x84000002, 4, smc_cpu_off},
|
||||
{0xC4000003, 1, smc_cpu_on},
|
||||
{0xC3000004, 1, smc_get_config_priv},
|
||||
{0xC3000005, 1, smc_get_random_bytes_for_priv},
|
||||
{0xC3000006, 1, smc_panic},
|
||||
{0xC3000007, 1, smc_configure_carveout},
|
||||
{0xC3000008, 1, smc_read_write_register}
|
||||
};
|
||||
#define SMC_PRIV_HANDLERS (sizeof(g_smc_priv_table) / sizeof(g_smc_priv_table[0]))
|
||||
|
||||
/* This is a table used for atmosphere-specific SMCs. */
|
||||
static smc_table_entry_t g_smc_ams_table[SMC_AMS_HANDLERS] = {
|
||||
{0, NULL},
|
||||
{0xF0000201, smc_ams_iram_copy},
|
||||
static smc_table_entry_t g_smc_ams_table[] = {
|
||||
{0, 4, NULL},
|
||||
{0xF0000201, 0, smc_ams_iram_copy},
|
||||
{0xF0000002, 0, smc_read_write_register},
|
||||
};
|
||||
#define SMC_AMS_HANDLERS (sizeof(g_smc_ams_table) / sizeof(g_smc_ams_table[0]))
|
||||
|
||||
static smc_table_t g_smc_tables[SMC_HANDLER_COUNT + 1] = {
|
||||
{ /* SMC_HANDLER_USER */
|
||||
@@ -177,6 +180,7 @@ void set_version_specific_smcs(void) {
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_600:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_620:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
/* No more LoadSecureExpModKey. */
|
||||
g_smc_user_table[0xE].handler = NULL;
|
||||
g_smc_user_table[0xC].id = 0xC300D60C;
|
||||
@@ -292,13 +296,17 @@ void call_smc_handler(uint32_t handler_id, smc_args_t *args) {
|
||||
#endif
|
||||
|
||||
/* Call function. */
|
||||
args->X[0] = smc_handler(args);
|
||||
if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800 ||
|
||||
(g_smc_tables[handler_id].handlers[smc_id].blacklist_mask & g_smc_blacklist_mask) == 0) {
|
||||
args->X[0] = smc_handler(args);
|
||||
} else {
|
||||
/* Call not allowed due to current boot conditions. */
|
||||
args->X[0] = 6;
|
||||
}
|
||||
|
||||
#if DEBUG_LOG_SMCS
|
||||
if (handler_id == SMC_HANDLER_USER) {
|
||||
*(volatile smc_args_t *)(get_iram_address_for_debug() + 0x100 + ((0x80 * num + 0x40) & 0x3FFF)) = *args;
|
||||
/*if (num >= 0x69) {
|
||||
panic(PANIC_REBOOT);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ static bool is_user_keyslot_valid(unsigned int keyslot) {
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_600:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_620:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
default:
|
||||
return keyslot <= 5;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ __attribute__ ((noreturn)) void panic(uint32_t code) {
|
||||
SAVE_SYSREG64(ELR_EL3, 0x18);
|
||||
SAVE_SYSREG64(FAR_EL3, 0x20);
|
||||
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x2;
|
||||
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10;
|
||||
*/
|
||||
MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x400ull) = 0x10; */
|
||||
|
||||
|
||||
/* TODO: Custom Panic Driver, which displays to screen without rebooting. */
|
||||
/* For now, just use NX BOOTLOADER's panic. */
|
||||
@@ -68,9 +68,9 @@ __attribute__ ((noreturn)) void panic_predefined(uint32_t which) {
|
||||
|
||||
__attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be)
|
||||
{
|
||||
if(as <= bs && bs <= ae)
|
||||
if(as <= bs && bs < ae)
|
||||
return true;
|
||||
if(bs <= as && as <= be)
|
||||
if(bs <= as && as < be)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ void init_dma_controllers(unsigned int target_firmware) {
|
||||
/* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */
|
||||
MAKE_REG32(0x50060000) |= 0x38000000;
|
||||
|
||||
/* AHB_ARBITRATION_DISABLE_0 - Disables USB and USB2 from arbitration */
|
||||
MAKE_REG32(0x6000C004) = 0x40040;
|
||||
/* AHB_ARBITRATION_DISABLE_0 - Disables USB, USB2, and AHB-DMA from arbitration */
|
||||
MAKE_REG32(0x6000C004) = 0x40060;
|
||||
|
||||
/* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */
|
||||
MAKE_REG32(0x6000C008) = 0xE0000001;
|
||||
|
||||
@@ -39,6 +39,17 @@ uintptr_t get_warmboot_main_stack_address(void) {
|
||||
return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x780;
|
||||
}
|
||||
|
||||
static void warmboot_configure_hiz_mode(void) {
|
||||
/* Enable input to I2C1 */
|
||||
PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40;
|
||||
PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40;
|
||||
|
||||
clkrst_reboot(CARDEVICE_I2C1);
|
||||
i2c_init(0);
|
||||
i2c_clear_ti_charger_bit_7();
|
||||
clkrst_disable(CARDEVICE_I2C1);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) warmboot_main(void) {
|
||||
/*
|
||||
This function and its callers are reached in either of the following events, under normal conditions:
|
||||
@@ -79,15 +90,10 @@ void __attribute__((noreturn)) warmboot_main(void) {
|
||||
/* Make PMC (2.x+), MC (4.x+) registers secure-only */
|
||||
secure_additional_devices();
|
||||
|
||||
if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400 || configitem_get_hardware_type() == 0) {
|
||||
/* Enable input to I2C1 */
|
||||
PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40;
|
||||
PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40;
|
||||
|
||||
clkrst_reboot(CARDEVICE_I2C1);
|
||||
i2c_init(0);
|
||||
i2c_clear_ti_charger_bit_7();
|
||||
clkrst_disable(CARDEVICE_I2C1);
|
||||
if ((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_400) ||
|
||||
((exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_800) && configitem_get_hardware_type() == 0) ||
|
||||
(configitem_is_hiz_mode_enabled())) {
|
||||
warmboot_configure_hiz_mode();
|
||||
}
|
||||
|
||||
clear_user_smc_in_progress();
|
||||
|
||||
@@ -90,9 +90,6 @@ static void setup_env(void) {
|
||||
/* Initialize hardware. */
|
||||
nx_hwinit();
|
||||
|
||||
/* Check for panics. */
|
||||
check_and_display_panic();
|
||||
|
||||
/* Zero-fill the framebuffer and register it as printk provider. */
|
||||
video_init(g_framebuffer);
|
||||
|
||||
@@ -138,6 +135,9 @@ int main(void) {
|
||||
|
||||
/* Initialize the display, console, etc. */
|
||||
setup_env();
|
||||
|
||||
/* Check for panics. */
|
||||
check_and_display_panic();
|
||||
|
||||
/* Load the BCT0 configuration ini off of the SD. */
|
||||
bct0 = load_config();
|
||||
|
||||
@@ -14,15 +14,78 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "panic.h"
|
||||
#include "di.h"
|
||||
#include "pmc.h"
|
||||
#include "fuse.h"
|
||||
#include "utils.h"
|
||||
#include "fs_utils.h"
|
||||
#include "lib/log.h"
|
||||
|
||||
static uint32_t g_panic_code = 0;
|
||||
|
||||
static const char *get_error_desc_str(uint32_t error_desc) {
|
||||
switch (error_desc) {
|
||||
case 0x100:
|
||||
return "Instruction Abort";
|
||||
case 0x101:
|
||||
return "Data Abort";
|
||||
case 0x102:
|
||||
return "PC Misalignment";
|
||||
case 0x103:
|
||||
return "SP Misalignment";
|
||||
case 0x104:
|
||||
return "Trap";
|
||||
case 0x106:
|
||||
return "SError";
|
||||
case 0x301:
|
||||
return "Bad SVC";
|
||||
case 0xFFE:
|
||||
return "std::abort() called";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
/* Copy fatal error context to the stack. */
|
||||
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
|
||||
|
||||
/* Change magic to invalid, to prevent double-display of error/bootlooping. */
|
||||
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic = 0xCCCCCCCC;
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "A fatal error occurred when running Atmosph\xe8re.\n");
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Title ID: %016llx\n", ctx.title_id);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Error Desc: %s (0x%x)\n", get_error_desc_str(ctx.error_desc), ctx.error_desc);
|
||||
|
||||
/* Save context to the SD card. */
|
||||
{
|
||||
char filepath[0x40];
|
||||
snprintf(filepath, sizeof(filepath) - 1, "/atmosphere/fatal_errors/report_%016llx.bin", ctx.report_identifier);
|
||||
filepath[sizeof(filepath)-1] = 0;
|
||||
write_to_file(&ctx, sizeof(ctx), filepath);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX,"Report saved to %s\n", filepath);
|
||||
}
|
||||
|
||||
/* Display error. */
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX,"\nPress POWER to reboot\n");
|
||||
}
|
||||
|
||||
wait_for_button_and_reboot();
|
||||
}
|
||||
|
||||
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. */
|
||||
/* In the case of our own panics, we assume that the display has already been initialized. */
|
||||
bool has_panic = APBDEV_PMC_RST_STATUS_0 != 0 || g_panic_code != 0;
|
||||
|
||||
@@ -28,6 +28,45 @@
|
||||
|
||||
#define PANIC_CODE_SAFEMODE 0x00000020
|
||||
|
||||
|
||||
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
|
||||
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
|
||||
|
||||
/* Atmosphere reboot-to-fatal-error. */
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t error_desc;
|
||||
uint64_t title_id;
|
||||
union {
|
||||
uint64_t gprs[32];
|
||||
struct {
|
||||
uint64_t _gprs[29];
|
||||
uint64_t fp;
|
||||
uint64_t lr;
|
||||
uint64_t sp;
|
||||
};
|
||||
};
|
||||
uint64_t pc;
|
||||
uint64_t module_base;
|
||||
uint32_t pstate;
|
||||
uint32_t afsr0;
|
||||
uint32_t afsr1;
|
||||
uint32_t esr;
|
||||
uint64_t far;
|
||||
uint64_t report_identifier; /* Normally just system tick. */
|
||||
uint64_t stack_trace_size;
|
||||
uint64_t stack_dump_size;
|
||||
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
|
||||
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
|
||||
} atmosphere_fatal_error_ctx;
|
||||
|
||||
/* "AFE1" */
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
|
||||
/* "AFE0" */
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641
|
||||
|
||||
#define ATMOSPHERE_FATAL_ERROR_CONTEXT ((volatile atmosphere_fatal_error_ctx *)(0x4003E000))
|
||||
|
||||
void check_and_display_panic(void);
|
||||
__attribute__ ((noreturn)) void panic(uint32_t code);
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ static int sdmmc_int_clk_enable(sdmmc_t *sdmmc)
|
||||
|
||||
/* Clock failed to stabilize. */
|
||||
if (is_timeout) {
|
||||
sdmmc_error(sdmmc, "clock never stabilized!");
|
||||
sdmmc_error(sdmmc, "Clock never stabilized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1202,6 +1202,9 @@ void sdmmc_finish(sdmmc_t *sdmmc)
|
||||
|
||||
/* Power cycle for 100ms without power. */
|
||||
mdelay(100);
|
||||
|
||||
/* Disable the regulator. */
|
||||
max77620_regulator_enable(REGULATOR_LDO2, 0);
|
||||
}
|
||||
|
||||
/* Force a register read to refresh the clock control value. */
|
||||
@@ -1350,8 +1353,11 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
||||
{
|
||||
/* Invalid block count or size. */
|
||||
if (!req->blksz || !req->num_blocks)
|
||||
{
|
||||
sdmmc_error(sdmmc, "Empty DMA request!");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
uint32_t blkcnt = req->num_blocks;
|
||||
|
||||
/* Truncate block count. Length can't be over 65536 bytes. */
|
||||
@@ -1363,8 +1369,11 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
||||
|
||||
/* DMA buffer address must be aligned to 4 bytes. */
|
||||
if ((4 - (dma_base_addr & 0x03)) & 0x03)
|
||||
{
|
||||
sdmmc_error(sdmmc, "Invalid DMA request data buffer: 0x%08X", dma_base_addr);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Write our address to the registers. */
|
||||
if (sdmmc->use_adma)
|
||||
{
|
||||
|
||||
@@ -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/ams_mitm
|
||||
export KIPDIRS := $(AMS)/stratosphere/loader $(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 boot_100.kip boot_200.kip
|
||||
KIPFILES := loader.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.enc \
|
||||
|
||||
@@ -220,10 +220,8 @@ SECTIONS
|
||||
======================= */
|
||||
PROVIDE(__ams_mitm_kip_start__ = ams_mitm_kip - __start__);
|
||||
PROVIDE(__ams_mitm_kip_size__ = ams_mitm_kip_end - ams_mitm_kip);
|
||||
PROVIDE(__boot_100_kip_start__ = boot_100_kip - __start__);
|
||||
PROVIDE(__boot_100_kip_size__ = boot_100_kip_end - boot_100_kip);
|
||||
PROVIDE(__boot_200_kip_start__ = boot_200_kip - __start__);
|
||||
PROVIDE(__boot_200_kip_size__ = boot_200_kip_end - boot_200_kip);
|
||||
PROVIDE(__boot_kip_start__ = boot_kip - __start__);
|
||||
PROVIDE(__boot_kip_size__ = boot_kip_end - boot_kip);
|
||||
PROVIDE(__exosphere_bin_start__ = exosphere_bin - __start__);
|
||||
PROVIDE(__exosphere_bin_size__ = exosphere_bin_end - exosphere_bin);
|
||||
PROVIDE(__fusee_primary_bin_start__ = fusee_primary_bin - __start__);
|
||||
@@ -242,6 +240,8 @@ SECTIONS
|
||||
PROVIDE(__sept_secondary_enc_size__ = sept_secondary_enc_end - sept_secondary_enc);
|
||||
PROVIDE(__sm_kip_start__ = sm_kip - __start__);
|
||||
PROVIDE(__sm_kip_size__ = sm_kip_end - sm_kip);
|
||||
PROVIDE(__spl_kip_start__ = spl_kip - __start__);
|
||||
PROVIDE(__spl_kip_size__ = spl_kip_end - spl_kip);
|
||||
PROVIDE(__splash_screen_bmp_start__ = splash_screen_bmp - __start__);
|
||||
PROVIDE(__splash_screen_bmp_size__ = splash_screen_bmp_end - splash_screen_bmp);
|
||||
PROVIDE(__thermosphere_bin_start__ = thermosphere_bin - __start__);
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
/* "EXO0" */
|
||||
#define MAGIC_EXOSPHERE_CONFIG (0x304F5845)
|
||||
|
||||
#define EXOSPHERE_FLAGS_DEFAULT 0x00000000
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
#define EXOSPHERE_FLAG_PERFORM_620_KEYGEN (1 << 0u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
|
||||
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
|
||||
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
|
||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
@@ -41,5 +42,6 @@ typedef struct {
|
||||
#define EXOSPHERE_TARGETFW_KEY "target_firmware"
|
||||
#define EXOSPHERE_DEBUGMODE_PRIV_KEY "debugmode"
|
||||
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
|
||||
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
|
||||
|
||||
#endif
|
||||
@@ -41,6 +41,10 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
uint8_t hash[0x20]; /* TODO: Come up with a better way to identify kernels, that doesn't rely on hashing them. */
|
||||
size_t hash_offset; /* Start hashing at offset N, if this is set. */
|
||||
size_t hash_size; /* Only hash the first N bytes of the kernel, if this is set. */
|
||||
size_t embedded_ini_offset; /* 8.0.0+ embeds the INI in kernel section. */
|
||||
size_t embedded_ini_ptr; /* 8.0.0+ embeds the INI in kernel section. */
|
||||
size_t free_code_space_offset;
|
||||
unsigned int num_patches;
|
||||
const kernel_patch_t *patches;
|
||||
@@ -366,11 +370,67 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_send)[] = {0xA9BF
|
||||
static const uint8_t MAKE_KERNEL_PATTERN_NAME(700, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||
|
||||
/*
|
||||
stp x10, x11, [sp, #-0x10]!
|
||||
ldr x11, [sp, #0x70]
|
||||
mov w10, w25
|
||||
lsl x10, x10, #2
|
||||
ldr x10, [x11, x10]
|
||||
mov x9, #0x0000ffffffffffff
|
||||
and x8, x10, x9
|
||||
mov x9, #0xffff000000000000
|
||||
and x10, x10, x9
|
||||
mov x9, #0xfffe000000000000
|
||||
cmp x10, x9
|
||||
beq #0x20
|
||||
|
||||
stp x8, x9, [sp, #-0x10]!
|
||||
ldr x8, [x21]
|
||||
ldr x8, [x8, #0x38]
|
||||
mov x0, x21
|
||||
blr x8
|
||||
ldp x8, x9, [sp],#0x10
|
||||
mov x8, x0
|
||||
|
||||
ldp x10, x11, [sp],#0x10
|
||||
mov x0, x8
|
||||
*/
|
||||
static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_send)[] = {0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x15, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x19, 0x2A, 0x39, 0x0B, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_send)[] = {0xA9BF2FEA, 0xF9403BEB, 0x2A1903EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||
/*
|
||||
stp x10, x11, [sp, #-0x10]!
|
||||
ldr x11, [sp, #0x98]
|
||||
mov w10, w22
|
||||
lsl x10, x10, #2
|
||||
ldr x10, [x11, x10]
|
||||
mov x9, #0x0000ffffffffffff
|
||||
and x8, x10, x9
|
||||
mov x9, #0xffff000000000000
|
||||
and x10, x10, x9
|
||||
mov x9, #0xfffe000000000000
|
||||
cmp x10, x9
|
||||
beq #0x20
|
||||
|
||||
stp x8, x9, [sp, #-0x10]!
|
||||
ldr x8, [x27]
|
||||
ldr x8, [x8, #0x38]
|
||||
mov x0, x27
|
||||
blr x8
|
||||
ldp x8, x9, [sp],#0x10
|
||||
mov x8, x0
|
||||
|
||||
ldp x10, x11, [sp],#0x10
|
||||
mov x0, x8
|
||||
*/
|
||||
static const uint8_t MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xA9, 0x83, 0x50, 0xF8, 0xE8, 0x03, 0x16, 0x2A, 0xD6, 0x0A, 0x00, 0x11};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404FEB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
|
||||
|
||||
/* svcControlCodeMemory Patches */
|
||||
/* b.eq -> nop */
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP};
|
||||
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP};
|
||||
|
||||
/* Hook Definitions. */
|
||||
static const kernel_patch_t g_kernel_patches_100[] = {
|
||||
@@ -532,6 +592,29 @@ static const kernel_patch_t g_kernel_patches_700[] = {
|
||||
.patch_offset = 0x3C6E0,
|
||||
}
|
||||
};
|
||||
static const kernel_patch_t g_kernel_patches_800[] = {
|
||||
{ /* Send Message Process ID Patch. */
|
||||
.pattern_size = 0x1C,
|
||||
.pattern = MAKE_KERNEL_PATTERN_NAME(800, proc_id_send),
|
||||
.pattern_hook_offset = 0x0,
|
||||
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, proc_id_send))/sizeof(instruction_t),
|
||||
.branch_back_offset = 0x10,
|
||||
.payload = MAKE_KERNEL_PATCH_NAME(800, proc_id_send)
|
||||
},
|
||||
{ /* Receive Message Process ID Patch. */
|
||||
.pattern_size = 0x1C,
|
||||
.pattern = MAKE_KERNEL_PATTERN_NAME(800, proc_id_recv),
|
||||
.pattern_hook_offset = 0x0,
|
||||
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, proc_id_recv))/sizeof(instruction_t),
|
||||
.branch_back_offset = 0x10,
|
||||
.payload = MAKE_KERNEL_PATCH_NAME(800, proc_id_recv)
|
||||
},
|
||||
{ /* svcControlCodeMemory Patch. */
|
||||
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory))/sizeof(instruction_t),
|
||||
.payload = MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory),
|
||||
.patch_offset = 0x3FAD0,
|
||||
}
|
||||
};
|
||||
|
||||
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
|
||||
|
||||
@@ -581,6 +664,15 @@ static const kernel_info_t g_kernel_infos[] = {
|
||||
.hash = {0xA2, 0x5E, 0x47, 0x0C, 0x8E, 0x6D, 0x2F, 0xD7, 0x5D, 0xAD, 0x24, 0xD7, 0xD8, 0x24, 0x34, 0xFB, 0xCD, 0x77, 0xBB, 0xE6, 0x66, 0x03, 0xCB, 0xAF, 0xAB, 0x85, 0x45, 0xA0, 0x91, 0xAF, 0x34, 0x25},
|
||||
.free_code_space_offset = 0x5FEC0,
|
||||
KERNEL_PATCHES(700)
|
||||
},
|
||||
{ /* 8.0.0. */
|
||||
.hash = {0xA6, 0xAD, 0x5D, 0x7F, 0xCF, 0x25, 0x80, 0xAE, 0xE6, 0x57, 0x9F, 0x6F, 0xC5, 0xC5, 0xF6, 0x13, 0x77, 0x23, 0xAC, 0x88, 0x79, 0x76, 0xF7, 0x25, 0x06, 0x16, 0x35, 0x3B, 0x3F, 0xA7, 0x59, 0x49},
|
||||
.hash_offset = 0x1A8,
|
||||
.hash_size = 0x95000 - 0x1A8,
|
||||
.embedded_ini_offset = 0x95000,
|
||||
.embedded_ini_ptr = 0x168,
|
||||
.free_code_space_offset = 0x607F0,
|
||||
KERNEL_PATCHES(800)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -607,17 +699,27 @@ uint8_t *search_pattern(void *_mem, size_t mem_size, const void *_pattern, size_
|
||||
|
||||
const kernel_info_t *get_kernel_info(void *kernel, size_t size) {
|
||||
uint8_t calculated_hash[0x20];
|
||||
uint8_t calculated_partial_hash[0x20];
|
||||
se_calculate_sha256(calculated_hash, kernel, size);
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(g_kernel_infos)/sizeof(kernel_info_t); i++) {
|
||||
if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) {
|
||||
return &g_kernel_infos[i];
|
||||
if (g_kernel_infos[i].hash_size == 0 || size <= g_kernel_infos[i].hash_size) {
|
||||
if (memcmp(calculated_hash, g_kernel_infos[i].hash, sizeof(calculated_hash)) == 0) {
|
||||
return &g_kernel_infos[i];
|
||||
}
|
||||
} else {
|
||||
se_calculate_sha256(calculated_partial_hash, (void *)((uintptr_t)kernel + g_kernel_infos[i].hash_offset), g_kernel_infos[i].hash_size);
|
||||
if (memcmp(calculated_partial_hash, g_kernel_infos[i].hash, sizeof(calculated_partial_hash)) == 0) {
|
||||
return &g_kernel_infos[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel) {
|
||||
void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel, void **out_ini1) {
|
||||
const kernel_info_t *kernel_info = get_kernel_info(_kernel, size);
|
||||
*out_ini1 = NULL;
|
||||
|
||||
/* Apply IPS patches. */
|
||||
apply_kernel_ips_patches(_kernel, size);
|
||||
@@ -631,6 +733,11 @@ void package2_patch_kernel(void *_kernel, size_t size, bool is_sd_kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kernel_info->embedded_ini_offset != 0) {
|
||||
*out_ini1 = (void *)((uintptr_t)_kernel + kernel_info->embedded_ini_offset);
|
||||
*((volatile uint64_t *)((uintptr_t)_kernel + kernel_info->embedded_ini_ptr)) = (uint64_t)size;
|
||||
}
|
||||
|
||||
/* Apply hooks and patches. */
|
||||
uint8_t *kernel = (uint8_t *)_kernel;
|
||||
size_t free_space_offset = kernel_info->free_code_space_offset;
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
void package2_patch_kernel(void *kernel, size_t kernel_size, bool is_sd_kernel);
|
||||
void package2_patch_kernel(void *kernel, size_t kernel_size, bool is_sd_kernel, void **out_ini1);
|
||||
|
||||
#endif
|
||||
@@ -149,6 +149,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
||||
desired_keyblob = MASTERKEY_REVISION_620;
|
||||
break;
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
desired_keyblob = MASTERKEY_REVISION_700_CURRENT;
|
||||
break;
|
||||
default:
|
||||
@@ -223,6 +224,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_600:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_620:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_700:
|
||||
case ATMOSPHERE_TARGET_FIRMWARE_800:
|
||||
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
|
||||
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
|
||||
decrypt_data_into_keyslot(0xE, 0xC, masterkey_4x_seed, 0x10);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
extern void (*__program_exit_callback)(int rc);
|
||||
|
||||
static __attribute__((__aligned__(0x200))) stage2_args_t g_stage2_args_store;
|
||||
static stage2_args_t *g_stage2_args;
|
||||
static bool g_do_nxboot;
|
||||
|
||||
@@ -59,10 +60,10 @@ static void setup_env(void) {
|
||||
train_dram();
|
||||
}
|
||||
|
||||
|
||||
static void cleanup_env(void) {
|
||||
/* Unmount everything (this causes all open files to be flushed and closed) */
|
||||
nxfs_unmount_all();
|
||||
//console_end();
|
||||
}
|
||||
|
||||
static void exit_callback(int rc) {
|
||||
@@ -84,7 +85,8 @@ int main(int argc, void **argv) {
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
g_stage2_args = (stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT];
|
||||
g_stage2_args = &g_stage2_args_store;
|
||||
memcpy(g_stage2_args, (stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT], sizeof(*g_stage2_args));
|
||||
|
||||
if (g_stage2_args->version != 0) {
|
||||
generic_panic();
|
||||
@@ -101,10 +103,11 @@ int main(int argc, void **argv) {
|
||||
|
||||
/* Load BCT0 from SD if needed. */
|
||||
if (strcmp(g_stage2_args->bct0, "") == 0) {
|
||||
read_from_file(g_stage2_args->bct0, sizeof(g_stage2_args->bct0) - 1, "atmosphere/BCT.ini");
|
||||
if (!read_from_file(g_stage2_args->bct0, sizeof(g_stage2_args->bct0) - 1, "atmosphere/BCT.ini")) {
|
||||
uint32_t bct_tmp_buf[sizeof(g_stage2_args->bct0) / sizeof(uint32_t)] = {0};
|
||||
if (!read_from_file(bct_tmp_buf, sizeof(bct_tmp_buf) - 1, "atmosphere/BCT.ini")) {
|
||||
fatal_error("Failed to read BCT0 from SD!\n");
|
||||
}
|
||||
memcpy(g_stage2_args->bct0, bct_tmp_buf, sizeof(bct_tmp_buf));
|
||||
}
|
||||
|
||||
/* This will load all remaining binaries off of the SD. */
|
||||
@@ -118,6 +121,8 @@ int main(int argc, void **argv) {
|
||||
uint32_t boot_memaddr = nxboot_main();
|
||||
/* Wait for the splash screen to have been displayed as long as it should be. */
|
||||
splash_screen_wait_delay();
|
||||
/* Cleanup environment. */
|
||||
cleanup_env();
|
||||
/* Finish boot. */
|
||||
nxboot_finish(boot_memaddr);
|
||||
} else {
|
||||
|
||||
@@ -106,22 +106,27 @@ static int exosphere_ini_handler(void *user, const char *section, const char *na
|
||||
if (strcmp(section, "exosphere") == 0) {
|
||||
if (strcmp(name, EXOSPHERE_TARGETFW_KEY) == 0) {
|
||||
sscanf(value, "%d", &exo_cfg->target_firmware);
|
||||
}
|
||||
if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
||||
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
|
||||
} else {
|
||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV);
|
||||
}
|
||||
}
|
||||
if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
|
||||
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
|
||||
} else {
|
||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
|
||||
}
|
||||
} else if (strcmp(name, EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
exo_cfg->flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
|
||||
} else {
|
||||
exo_cfg->flags &= ~(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@@ -170,8 +175,10 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||
fatal_error("[NXBOOT]: Unable to identify package1!\n");
|
||||
}
|
||||
}
|
||||
case 0x0F:
|
||||
case 0x0F: /* 7.0.0 - 7.0.1 */
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_700;
|
||||
case 0x10: /* 8.0.0 */
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_800;
|
||||
default:
|
||||
fatal_error("[NXBOOT]: Unable to identify package1!\n");
|
||||
}
|
||||
@@ -412,7 +419,7 @@ uint32_t nxboot_main(void) {
|
||||
if (!package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size)) {
|
||||
fatal_error("[NXBOOT]: Failed to read the TSEC firmware from Package1loader!\n");
|
||||
}
|
||||
if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_700) {
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) {
|
||||
tsec_fw_size = 0x3000;
|
||||
} else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_620) {
|
||||
tsec_fw_size = 0x2900;
|
||||
|
||||
@@ -52,7 +52,8 @@ SdmmcPartitionNum g_current_emmc_partition = SDMMC_PARTITION_INVALID;
|
||||
static int mmc_partition_initialize(device_partition_t *devpart) {
|
||||
mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct;
|
||||
|
||||
if (devpart->read_cipher != NULL || devpart->write_cipher != NULL) {
|
||||
/* Allocate the crypto work buffer. */
|
||||
if ((devpart->read_cipher != NULL) || (devpart->write_cipher != NULL)) {
|
||||
devpart->crypto_work_buffer = memalign(16, devpart->sector_size * 16);
|
||||
if (devpart->crypto_work_buffer == NULL) {
|
||||
return ENOMEM;
|
||||
@@ -70,6 +71,7 @@ static int mmc_partition_initialize(device_partition_t *devpart) {
|
||||
g_ahb_redirect_enabled = true;
|
||||
}
|
||||
|
||||
/* Initialize hardware. */
|
||||
if (mmcpart->device == &g_sd_device) {
|
||||
if (!g_sd_device_initialized) {
|
||||
int rc = sdmmc_device_sd_init(mmcpart->device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_SDR104) ? 0 : EIO;
|
||||
@@ -94,13 +96,33 @@ static int mmc_partition_initialize(device_partition_t *devpart) {
|
||||
}
|
||||
|
||||
static void mmc_partition_finalize(device_partition_t *devpart) {
|
||||
free(devpart->crypto_work_buffer);
|
||||
mmc_partition_info_t *mmcpart = (mmc_partition_info_t *)devpart->device_struct;
|
||||
|
||||
/* Finalize hardware. */
|
||||
if (mmcpart->device == &g_sd_device) {
|
||||
if (g_sd_device_initialized) {
|
||||
sdmmc_device_finish(&g_sd_device);
|
||||
g_sd_device_initialized = false;
|
||||
}
|
||||
devpart->initialized = false;
|
||||
} else if (mmcpart->device == &g_emmc_device) {
|
||||
if (g_emmc_device_initialized) {
|
||||
sdmmc_device_finish(&g_emmc_device);
|
||||
g_emmc_device_initialized = false;
|
||||
}
|
||||
devpart->initialized = false;
|
||||
}
|
||||
|
||||
/* Disable AHB redirection if necessary. */
|
||||
if (g_ahb_redirect_enabled) {
|
||||
mc_disable_ahb_redirect();
|
||||
g_ahb_redirect_enabled = false;
|
||||
}
|
||||
|
||||
/* Free the crypto work buffer. */
|
||||
if (devpart->crypto_work_buffer != NULL) {
|
||||
free(devpart->crypto_work_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int mmc_partition_read(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <atmosphere.h>
|
||||
#include "utils.h"
|
||||
#include "masterkey.h"
|
||||
#include "stratosphere.h"
|
||||
@@ -86,10 +87,22 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
||||
}
|
||||
|
||||
/* Perform any patches we want to the NX kernel. */
|
||||
package2_patch_kernel(kernel, kernel_size, is_sd_kernel);
|
||||
|
||||
package2_patch_kernel(kernel, kernel_size, is_sd_kernel, (void *)&orig_ini1);
|
||||
|
||||
/* Ensure we know where embedded INI is if present, and we don't if not. */
|
||||
if ((target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 != NULL) ||
|
||||
(target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 == NULL)) {
|
||||
fatal_error("Error: inappropriate kernel embedded ini context");
|
||||
}
|
||||
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilding the INI1 section...\n");
|
||||
package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1);
|
||||
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800) {
|
||||
package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1);
|
||||
} else {
|
||||
/* On 8.0.0, place INI1 right after kernelldr for our sanity. */
|
||||
package2->metadata.section_offsets[PACKAGE2_SECTION_INI1] = package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL] + package2->metadata.section_sizes[PACKAGE2_SECTION_KERNEL];
|
||||
}
|
||||
|
||||
/* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */
|
||||
rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware);
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilt INI1...\n");
|
||||
@@ -187,10 +200,15 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[]
|
||||
}
|
||||
|
||||
/* Ensure no overlap with later sections. */
|
||||
for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) {
|
||||
uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section];
|
||||
if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) {
|
||||
return false;
|
||||
if (metadata->section_sizes[section] != 0) {
|
||||
for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) {
|
||||
if (metadata->section_sizes[later_section] == 0) {
|
||||
continue;
|
||||
}
|
||||
uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section];
|
||||
if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ static int sdmmc_int_clk_enable(sdmmc_t *sdmmc)
|
||||
|
||||
/* Clock failed to stabilize. */
|
||||
if (is_timeout) {
|
||||
sdmmc_error(sdmmc, "clock never stabilized!");
|
||||
sdmmc_error(sdmmc, "Clock never stabilized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1202,6 +1202,9 @@ void sdmmc_finish(sdmmc_t *sdmmc)
|
||||
|
||||
/* Power cycle for 100ms without power. */
|
||||
mdelay(100);
|
||||
|
||||
/* Disable the regulator. */
|
||||
max77620_regulator_enable(REGULATOR_LDO2, 0);
|
||||
}
|
||||
|
||||
/* Force a register read to refresh the clock control value. */
|
||||
@@ -1350,8 +1353,11 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
||||
{
|
||||
/* Invalid block count or size. */
|
||||
if (!req->blksz || !req->num_blocks)
|
||||
{
|
||||
sdmmc_error(sdmmc, "Empty DMA request!");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
uint32_t blkcnt = req->num_blocks;
|
||||
|
||||
/* Truncate block count. Length can't be over 65536 bytes. */
|
||||
@@ -1363,8 +1369,11 @@ static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
|
||||
|
||||
/* DMA buffer address must be aligned to 4 bytes. */
|
||||
if ((4 - (dma_base_addr & 0x03)) & 0x03)
|
||||
{
|
||||
sdmmc_error(sdmmc, "Invalid DMA request data buffer: 0x%08X", dma_base_addr);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Write our address to the registers. */
|
||||
if (sdmmc->use_adma)
|
||||
{
|
||||
|
||||
@@ -103,20 +103,12 @@ _content_headers:
|
||||
.asciz "ams_mitm"
|
||||
.align 5
|
||||
|
||||
/* boot_100 content header */
|
||||
.word __boot_100_kip_start__
|
||||
.word __boot_100_kip_size__
|
||||
/* boot content header */
|
||||
.word __boot_kip_start__
|
||||
.word __boot_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "boot_100"
|
||||
.align 5
|
||||
|
||||
/* boot_200 content header */
|
||||
.word __boot_200_kip_start__
|
||||
.word __boot_200_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "boot_200"
|
||||
.asciz "boot"
|
||||
.align 5
|
||||
|
||||
/* exosphere content header */
|
||||
@@ -191,6 +183,14 @@ _content_headers:
|
||||
.asciz "sm"
|
||||
.align 5
|
||||
|
||||
/* spl content header */
|
||||
.word __spl_kip_start__
|
||||
.word __spl_kip_size__
|
||||
.word CONTENT_TYPE_KIP
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "spl"
|
||||
.align 5
|
||||
|
||||
/* splash_screen content header */
|
||||
.word __splash_screen_bmp_start__
|
||||
.word __splash_screen_bmp_size__
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
#include "pm_kip.h"
|
||||
#include "sm_kip.h"
|
||||
#include "ams_mitm_kip.h"
|
||||
#include "boot_100_kip.h"
|
||||
#include "boot_200_kip.h"
|
||||
#include "boot_kip.h"
|
||||
#include "spl_kip.h"
|
||||
#undef u8
|
||||
#undef u32
|
||||
|
||||
@@ -45,18 +45,15 @@ static bool g_stratosphere_loader_enabled = true;
|
||||
static bool g_stratosphere_sm_enabled = true;
|
||||
static bool g_stratosphere_pm_enabled = true;
|
||||
static bool g_stratosphere_ams_mitm_enabled = true;
|
||||
static bool g_stratosphere_boot_enabled = false;
|
||||
static bool g_stratosphere_spl_enabled = true;
|
||||
static bool g_stratosphere_boot_enabled = true;
|
||||
|
||||
extern const uint8_t boot_100_kip[], boot_200_kip[];
|
||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], ams_mitm_kip[];
|
||||
extern const uint32_t boot_100_kip_size, boot_200_kip_size;
|
||||
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, ams_mitm_kip_size;
|
||||
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;
|
||||
|
||||
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
||||
|
||||
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
const uint8_t *boot_kip = NULL;
|
||||
uint32_t boot_kip_size = 0;
|
||||
uint32_t num_processes = 0;
|
||||
uint8_t *data;
|
||||
|
||||
@@ -64,14 +61,6 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
return g_stratosphere_ini1;
|
||||
}
|
||||
|
||||
if (target_firmware <= ATMOSPHERE_TARGET_FIRMWARE_100) {
|
||||
boot_kip = boot_100_kip;
|
||||
boot_kip_size = boot_100_kip_size;
|
||||
} else {
|
||||
boot_kip = boot_200_kip;
|
||||
boot_kip_size = boot_200_kip_size;
|
||||
}
|
||||
|
||||
size_t size = sizeof(ini1_header_t);
|
||||
|
||||
/* Calculate our processes' sizes. */
|
||||
@@ -90,6 +79,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
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++;
|
||||
@@ -129,6 +123,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
data += sm_kip_size;
|
||||
}
|
||||
|
||||
if (g_stratosphere_spl_enabled) {
|
||||
memcpy(data, spl_kip, spl_kip_size);
|
||||
data += spl_kip_size;
|
||||
}
|
||||
|
||||
if (g_stratosphere_ams_mitm_enabled) {
|
||||
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
|
||||
data += ams_mitm_kip_size;
|
||||
|
||||
@@ -168,9 +168,9 @@ __attribute__((noreturn)) void fatal_error(const char *fmt, ...) {
|
||||
|
||||
__attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be)
|
||||
{
|
||||
if(as <= bs && bs <= ae)
|
||||
if(as <= bs && bs < ae)
|
||||
return true;
|
||||
if(bs <= as && as <= be)
|
||||
if(bs <= as && as < be)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
152
mesosphere/Makefile
Normal file
152
mesosphere/Makefile
Normal file
@@ -0,0 +1,152 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(MESOSPHERE_BOARD),)
|
||||
export MESOSPHERE_BOARD := nintendo-switch
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
export CONFIG_DIR := $(CURDIR)/config
|
||||
ifeq ($(MESOSPHERE_BOARD),nintendo-switch)
|
||||
export BOARD_MAKE_DIR := $(CURDIR)/config/board/nintendo/switch
|
||||
export ARCH_MAKE_DIR := $(CURDIR)/config/arch/arm64
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(CONFIG_DIR)/rules.mk
|
||||
include $(CONFIG_DIR)/common.mk
|
||||
include $(ARCH_MAKE_DIR)/arch.mk
|
||||
include $(BOARD_MAKE_DIR)/board.mk
|
||||
|
||||
SOURCES := $(COMMON_SOURCES_DIRS) $(ARCH_SOURCE_DIRS) $(BOARD_SOURCE_DIRS)
|
||||
DATA := data
|
||||
INCLUDES := include ../common/include
|
||||
|
||||
DEFINES := $(COMMON_DEFINES) $(ARCH_DEFINES) $(BOARD_DEFINES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
SETTING := $(COMMON_SETTING) $(ARCH_SETTING) $(BOARD_SETTING)
|
||||
|
||||
CFLAGS := $(SETTING) $(DEFINES) $(COMMON_CFLAGS) $(ARCH_CFLAGS) $(BOARD_CFLAGS)
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
CXXFLAGS := $(CFLAGS) $(COMMON_CXXFLAGS) $(ARCH_CXXFLAGS) $(BOARD_CXXFLAGS)
|
||||
|
||||
ASFLAGS := -g $(SETTING)
|
||||
LDFLAGS = -specs=$(ARCH_MAKE_DIR)/linker.specs $(SETTING) $(COMMON_LDFLAGS)
|
||||
|
||||
LIBS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
%.elf:
|
||||
@echo linking $(notdir $@)
|
||||
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
9
mesosphere/README.md
Normal file
9
mesosphere/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Mesosphère
|
||||
|
||||
**WORK IN PROGRESS**
|
||||
|
||||
Special thanks to:
|
||||
|
||||
* @gdkchan ([Ryujinx](https://github.com/Ryujinx/Ryujinx)'s author), without whom I would have been unable to understand many complex mechanisms of the Horizon kernel, such as the scheduler, etc. Ryujinx's kernel HLE is pretty accurate, and of course part of this work has strong similarites to Ryujinx's kernel code.
|
||||
* @fincs, who helped me in the kernel reverse-engineering process a lot as well, and with countless other things too.
|
||||
|
||||
25
mesosphere/config/arch/arm64/arch.mk
Normal file
25
mesosphere/config/arch/arm64/arch.mk
Normal file
@@ -0,0 +1,25 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
|
||||
PREFIX := aarch64-none-elf-
|
||||
|
||||
export CC := $(PREFIX)gcc
|
||||
export CXX := $(PREFIX)g++
|
||||
export AS := $(PREFIX)as
|
||||
export AR := $(PREFIX)gcc-ar
|
||||
export OBJCOPY := $(PREFIX)objcopy
|
||||
|
||||
ISVC=$(or $(VCBUILDHELPER_COMMAND),$(MSBUILDEXTENSIONSPATH32),$(MSBUILDEXTENSIONSPATH))
|
||||
|
||||
ifneq (,$(ISVC))
|
||||
ERROR_FILTER := 2>&1 | sed -e 's/\(.[a-zA-Z]\+\):\([0-9]\+\):/\1(\2):/g'
|
||||
endif
|
||||
|
||||
else
|
||||
include $(DEVKITPRO)/devkitA64/base_tools
|
||||
endif
|
||||
|
||||
ARCH_SETTING := -march=armv8-a -mgeneral-regs-only
|
||||
ARCH_DEFINES := -DMESOSPHERE_ARCH_ARM64
|
||||
ARCH_CFLAGS :=
|
||||
ARCH_CXXFLAGS :=
|
||||
ARCH_SOURCE_DIRS := source/arch/arm64
|
||||
214
mesosphere/config/arch/arm64/linker.ld
Normal file
214
mesosphere/config/arch/arm64/linker.ld
Normal file
@@ -0,0 +1,214 @@
|
||||
OUTPUT_ARCH(aarch64)
|
||||
ENTRY(_start)
|
||||
|
||||
/* TODO overhaul */
|
||||
|
||||
PHDRS
|
||||
{
|
||||
code PT_LOAD FLAGS(5) /* Read | Execute */;
|
||||
rodata PT_LOAD FLAGS(4) /* Read */;
|
||||
data PT_LOAD FLAGS(6) /* Read | Write */;
|
||||
dyn PT_DYNAMIC;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* =========== CODE section =========== */
|
||||
PROVIDE(__start__ = 0x0);
|
||||
. = __start__;
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0))
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.init :
|
||||
{
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
/* =========== RODATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
. = ALIGN(8);
|
||||
} :rodata
|
||||
|
||||
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } :rodata
|
||||
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata
|
||||
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata
|
||||
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata
|
||||
|
||||
.dynamic : { *(.dynamic) } :rodata :dyn
|
||||
.dynsym : { *(.dynsym) } :rodata
|
||||
.dynstr : { *(.dynstr) } :rodata
|
||||
.rela.dyn : { *(.rela.*) } :rodata
|
||||
.interp : { *(.interp) } :rodata
|
||||
.hash : { *(.hash) } :rodata
|
||||
.gnu.hash : { *(.gnu.hash) } :rodata
|
||||
.gnu.version : { *(.gnu.version) } :rodata
|
||||
.gnu.version_d : { *(.gnu.version_d) } :rodata
|
||||
.gnu.version_r : { *(.gnu.version_r) } :rodata
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) } :rodata
|
||||
|
||||
/* =========== DATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data
|
||||
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data
|
||||
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data
|
||||
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data
|
||||
|
||||
.tdata ALIGN(8) :
|
||||
{
|
||||
__tdata_lma = .;
|
||||
*(.tdata .tdata.* .gnu.linkonce.td.*)
|
||||
. = ALIGN(8);
|
||||
__tdata_lma_end = .;
|
||||
} :data
|
||||
|
||||
.tbss ALIGN(8) :
|
||||
{
|
||||
*(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon)
|
||||
. = ALIGN(8);
|
||||
} :data
|
||||
|
||||
.preinit_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} :data
|
||||
|
||||
.init_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} :data
|
||||
|
||||
.fini_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} :data
|
||||
|
||||
.ctors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} :data
|
||||
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} :data
|
||||
|
||||
__got_start__ = .;
|
||||
|
||||
.got : { *(.got) *(.igot) } :data
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) } :data
|
||||
|
||||
__got_end__ = .;
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} :data
|
||||
|
||||
__bss_start__ = .;
|
||||
.bss ALIGN(8) :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(8);
|
||||
|
||||
/* Reserve space for the TLS segment of the main thread */
|
||||
__tls_start = .;
|
||||
. += + SIZEOF(.tdata) + SIZEOF(.tbss);
|
||||
__tls_end = .;
|
||||
} : data
|
||||
__bss_end__ = .;
|
||||
|
||||
__end__ = ABSOLUTE(.) ;
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
__argdata__ = ABSOLUTE(.) ;
|
||||
|
||||
/* ==================
|
||||
==== Metadata ====
|
||||
================== */
|
||||
|
||||
/* Discard sections that difficult post-processing */
|
||||
/DISCARD/ : { *(.group .comment .note) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
}
|
||||
4
mesosphere/config/arch/arm64/linker.specs
Normal file
4
mesosphere/config/arch/arm64/linker.specs
Normal file
@@ -0,0 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(ARCH_MAKE_DIR /linker.ld) -pie --gc-sections -z text -z nodynamic-undefined-weak --build-id=sha1
|
||||
5
mesosphere/config/board/nintendo/switch/board.mk
Normal file
5
mesosphere/config/board/nintendo/switch/board.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
BOARD_SETTING := -mtune=cortex-a57
|
||||
BOARD_DEFINES := -DMESOSPHERE_BOARD_NINTENDO_SWITCH -DMESOSPHERE_BOARD_COMMON_ARM_ARM64_CLOCK
|
||||
BOARD_CFLAGS :=
|
||||
BOARD_CXXFLAGS :=
|
||||
BOARD_SOURCE_DIRS :=
|
||||
9
mesosphere/config/common.mk
Normal file
9
mesosphere/config/common.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
COMMON_DEFINES := -DBOOST_DISABLE_ASSERTS
|
||||
COMMON_SOURCES_DIRS := source/core source/interfaces source/interrupts source/kresources\
|
||||
source/processes source/threading source
|
||||
COMMON_SETTING := -fPIE -g -nostdlib
|
||||
COMMON_CFLAGS := -Wall -Werror -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv\
|
||||
-fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector
|
||||
COMMON_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++17
|
||||
COMMON_ASFLAGS :=
|
||||
COMMON_LDFLAGS := -Wl,-Map,out.map
|
||||
37
mesosphere/config/rules.mk
Normal file
37
mesosphere/config/rules.mk
Normal file
@@ -0,0 +1,37 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
%.a:
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $@)
|
||||
@rm -f $@
|
||||
$(AR) -rc $@ $^
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.c
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.s
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.S
|
||||
@echo $(notdir $<)
|
||||
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# canned command sequence for binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`"_end[];" > `(echo $(<F) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`"[];" >> `(echo $(<F) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' -e 's/[^A-Za-z0-9_]/_/g')`_size";" >> `(echo $(<F) | tr . _)`.h
|
||||
endef
|
||||
11
mesosphere/include/mesosphere/arch/KInterruptMaskGuard.hpp
Normal file
11
mesosphere/include/mesosphere/arch/KInterruptMaskGuard.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/KInterruptMaskGuard.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
11
mesosphere/include/mesosphere/arch/KSpinLock.hpp
Normal file
11
mesosphere/include/mesosphere/arch/KSpinLock.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/KSpinLock.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
11
mesosphere/include/mesosphere/arch/arch.hpp
Normal file
11
mesosphere/include/mesosphere/arch/arch.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// Dummy. Should be platform-independent:
|
||||
|
||||
class KInterruptMaskGuard final {
|
||||
public:
|
||||
|
||||
using FlagsType = u64;
|
||||
|
||||
KInterruptMaskGuard()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
}
|
||||
|
||||
~KInterruptMaskGuard()
|
||||
{
|
||||
RestoreInterrupts(flags);
|
||||
}
|
||||
|
||||
KInterruptMaskGuard(const KInterruptMaskGuard &) = delete;
|
||||
KInterruptMaskGuard(KInterruptMaskGuard &&) = delete;
|
||||
KInterruptMaskGuard &operator=(const KInterruptMaskGuard &) = delete;
|
||||
KInterruptMaskGuard &operator=(KInterruptMaskGuard &&) = delete;
|
||||
|
||||
private:
|
||||
u64 flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
103
mesosphere/include/mesosphere/arch/arm64/KSpinLock.hpp
Normal file
103
mesosphere/include/mesosphere/arch/arm64/KSpinLock.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// This largely uses the Linux kernel spinlock code, which is more efficient than Nintendo's (serializing two u16s into an u32).
|
||||
class KSpinLock {
|
||||
|
||||
private:
|
||||
|
||||
struct alignas(4) Ticket {
|
||||
u16 owner, next;
|
||||
};
|
||||
|
||||
Ticket ticket;
|
||||
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
u32 tmp;
|
||||
Ticket lockval;
|
||||
|
||||
asm volatile(
|
||||
" prfm pstl1strm, %2\n"
|
||||
"1: ldaxr %w0, %2\n"
|
||||
" eor %w1, %w0, %w0, ror #16\n"
|
||||
" cbnz %w1, 2f\n"
|
||||
" add %w0, %w0, %3\n"
|
||||
" stxr %w1, %w0, %2\n"
|
||||
" cbnz %w1, 1b\n"
|
||||
"2:"
|
||||
: "=&r" (lockval), "=&r" (tmp), "+Q" (ticket)
|
||||
: "I" (1 << 16)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return !tmp;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
u32 tmp;
|
||||
Ticket lockval, newval;
|
||||
|
||||
asm volatile(
|
||||
// Atomically increment the next ticket.
|
||||
" prfm pstl1strm, %3\n"
|
||||
"1: ldaxr %w0, %3\n"
|
||||
" add %w1, %w0, %w5\n"
|
||||
" stxr %w2, %w1, %3\n"
|
||||
" cbnz %w2, 1b\n"
|
||||
|
||||
|
||||
// Did we get the lock?
|
||||
" eor %w1, %w0, %w0, ror #16\n"
|
||||
" cbz %w1, 3f\n"
|
||||
/*
|
||||
No: spin on the owner. Send a local event to avoid missing an
|
||||
unlock before the exclusive load.
|
||||
*/
|
||||
" sevl\n"
|
||||
"2: wfe\n"
|
||||
" ldaxrh %w2, %4\n"
|
||||
" eor %w1, %w2, %w0, lsr #16\n"
|
||||
" cbnz %w1, 2b\n"
|
||||
// We got the lock. Critical section starts here.
|
||||
"3:"
|
||||
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*&ticket)
|
||||
: "Q" (ticket.owner), "I" (1 << 16)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
u64 tmp;
|
||||
asm volatile(
|
||||
" ldrh %w1, %0\n"
|
||||
" add %w1, %w1, #1\n"
|
||||
" stlrh %w1, %0"
|
||||
: "=Q" (ticket.owner), "=&r" (tmp)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
|
||||
}
|
||||
KSpinLock() = default;
|
||||
KSpinLock(const KSpinLock &) = delete;
|
||||
KSpinLock(KSpinLock &&) = delete;
|
||||
KSpinLock &operator=(const KSpinLock &) = delete;
|
||||
KSpinLock &operator=(KSpinLock &&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
101
mesosphere/include/mesosphere/arch/arm64/arch.hpp
Normal file
101
mesosphere/include/mesosphere/arch/arm64/arch.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/preprocessor.hpp>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
#define MESOSPHERE_READ_SYSREG(r) ({\
|
||||
u64 __val; \
|
||||
asm volatile("mrs %0, " BOOST_PP_STRINGIZE(r) : "=r" (__val)); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
#define MESOSPHERE_WRITE_SYSREG(v, r) do { \
|
||||
u64 __val = (u64)v; \
|
||||
asm volatile("msr " BOOST_PP_STRINGIZE(r) ", %0" \
|
||||
:: "r" (__val) : "memory"); \
|
||||
} while (false)
|
||||
|
||||
#define MESOSPHERE_DAIF_BIT(v) (((u64)(v)) >> 6)
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KCoreContext;
|
||||
|
||||
inline namespace arch
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
enum PsrMode {
|
||||
PSR_MODE_EL0t = 0x0u,
|
||||
PSR_MODE_EL1t = 0x4u,
|
||||
PSR_MODE_EL1h = 0x5u,
|
||||
PSR_MODE_EL2t = 0x8u,
|
||||
PSR_MODE_EL2h = 0x9u,
|
||||
PSR_MODE_EL3t = 0xCu,
|
||||
PSR_MODE_EL3h = 0xDu,
|
||||
PSR_MODE_MASK = 0xFu,
|
||||
PSR_MODE32_BIT = 0x10u,
|
||||
};
|
||||
|
||||
enum PsrInterruptBit {
|
||||
PSR_F_BIT = 1u << 6,
|
||||
PSR_I_BIT = 1u << 7,
|
||||
PSR_A_BIT = 1u << 8,
|
||||
PSR_D_BIT = 1u << 9,
|
||||
};
|
||||
|
||||
enum PsrStatusBit {
|
||||
PSR_PAN_BIT = 1u << 22,
|
||||
PSR_UAO_BIT = 1u << 23,
|
||||
};
|
||||
|
||||
enum PsrFlagBit {
|
||||
PSR_V_BIT = 1u << 28,
|
||||
PSR_C_BIT = 1u << 29,
|
||||
PSR_Z_BIT = 1u << 30,
|
||||
PSR_N_BIT = 1u << 31,
|
||||
};
|
||||
|
||||
enum PsrBitGroup {
|
||||
PSR_c = 0x000000FFu,
|
||||
PSR_x = 0x0000FF00u,
|
||||
PSR_s = 0x00FF0000u,
|
||||
PSR_f = 0xFF000000u,
|
||||
};
|
||||
|
||||
using InterruptFlagsType = u64;
|
||||
|
||||
static inline InterruptFlagsType MaskInterrupts()
|
||||
{
|
||||
InterruptFlagsType flags = MESOSPHERE_READ_SYSREG(daif);
|
||||
MESOSPHERE_WRITE_SYSREG(flags | PSR_I_BIT, daif);
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void RestoreInterrupts(InterruptFlagsType flags)
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG(MESOSPHERE_READ_SYSREG(daif) | (flags & PSR_I_BIT), daif);
|
||||
}
|
||||
|
||||
static inline KCoreContext &GetCurrentCoreContextInstance()
|
||||
{
|
||||
register KCoreContext *x18 asm ("x18");
|
||||
return *x18;
|
||||
}
|
||||
|
||||
static inline void ReloadCurrentCoreContextInstance()
|
||||
{
|
||||
asm volatile("mrs x18, tpidr_el1" ::: "x18", "memory");
|
||||
}
|
||||
|
||||
static inline void SetCurrentCoreContextInstance(KCoreContext &cctx)
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG((uiptr)&cctx, tpidr_el1);
|
||||
ReloadCurrentCoreContextInstance();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
11
mesosphere/include/mesosphere/board/KSystemClock.hpp
Normal file
11
mesosphere/include/mesosphere/board/KSystemClock.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if 1 //defined MESOSPHERE_ARCH_ARM64
|
||||
|
||||
#include <mesosphere/board/common/arm/arm64/timer/KSystemClock.hpp>
|
||||
|
||||
#else
|
||||
|
||||
//#error "No arch defined"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arm64/arch.hpp>
|
||||
|
||||
#ifndef MESOSPHERE_SYSTEM_CLOCK_RATE // NEEDS to be defined; depends on cntfreq
|
||||
#define MESOSPHERE_SYSTEM_CLOCK_RATE 192000000ull
|
||||
#endif
|
||||
|
||||
// Architectural aarch64 armv8 timer
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
inline namespace board
|
||||
{
|
||||
inline namespace common
|
||||
{
|
||||
inline namespace arm
|
||||
{
|
||||
inline namespace arm64
|
||||
{
|
||||
|
||||
// Dummy implementation
|
||||
// Needs to be changed for platform stuff
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/// Fulfills Clock named requirements
|
||||
class KSystemClock {
|
||||
public:
|
||||
|
||||
using rep = s64;
|
||||
using period = std::ratio<1, MESOSPHERE_SYSTEM_CLOCK_RATE>;
|
||||
using duration = std::chrono::duration<rep, period>;
|
||||
using time_point = std::chrono::time_point<KSystemClock>;
|
||||
|
||||
static constexpr bool is_steady = true;
|
||||
|
||||
static time_point now()
|
||||
{
|
||||
return time_point{duration::zero()};
|
||||
}
|
||||
|
||||
static constexpr bool isCorePrivate = true;
|
||||
static constexpr duration forever = duration{-1};
|
||||
static constexpr time_point never = time_point{forever};
|
||||
|
||||
static constexpr uint GetIrqId() { return 30; }
|
||||
|
||||
static void Disable()
|
||||
{
|
||||
// Note: still continues counting.
|
||||
MESOSPHERE_WRITE_SYSREG(0, cntp_ctl_el0);
|
||||
}
|
||||
|
||||
static void SetInterruptMasked(bool maskInterrupts)
|
||||
{
|
||||
u64 val = maskInterrupts ? 3 : 1; // Note: also enables the timer.
|
||||
MESOSPHERE_WRITE_SYSREG(val, cntp_ctl_el0);
|
||||
}
|
||||
|
||||
static void SetAlarm(const time_point &when)
|
||||
{
|
||||
u64 val = (u64)when.time_since_epoch().count();
|
||||
MESOSPHERE_WRITE_SYSREG(val, cntp_cval_el0);
|
||||
SetInterruptMasked(false);
|
||||
}
|
||||
|
||||
static void Initialize()
|
||||
{
|
||||
MESOSPHERE_WRITE_SYSREG(1, cntkctl_el1); // Trap register accesses from el0.
|
||||
Disable();
|
||||
MESOSPHERE_WRITE_SYSREG(UINT64_MAX, cntp_cval_el0);
|
||||
SetInterruptMasked(true);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
mesosphere/include/mesosphere/core/Handle.hpp
Normal file
29
mesosphere/include/mesosphere/core/Handle.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class Handle final {
|
||||
public:
|
||||
constexpr bool IsAliasOrFree() const { return isAlias || id < 0; }
|
||||
|
||||
constexpr bool operator==(const Handle &other) const
|
||||
{
|
||||
return index == other.index && id == other.id && isAlias == other.isAlias;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Handle &other) const { return !(*this == other); }
|
||||
|
||||
constexpr Handle() : index{0}, id{0}, isAlias{false} {}
|
||||
|
||||
private:
|
||||
friend class KHandleTable;
|
||||
constexpr Handle(u16 index, s16 id, bool isAlias = false) : index{index}, id{id}, isAlias{isAlias} {}
|
||||
u32 index : 15;
|
||||
s32 id : 16;
|
||||
u32 isAlias : 1;
|
||||
};
|
||||
|
||||
}
|
||||
165
mesosphere/include/mesosphere/core/KAutoObject.hpp
Normal file
165
mesosphere/include/mesosphere/core/KAutoObject.hpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_TRAITS(BaseId, DerivedId)\
|
||||
using BaseClass = K##BaseId ;\
|
||||
static constexpr KAutoObject::TypeId typeId = KAutoObject::TypeId::DerivedId;\
|
||||
virtual ushort GetClassToken() const\
|
||||
{\
|
||||
return KAutoObject::GenerateClassToken<K##DerivedId >();\
|
||||
}\
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_FW_DECL(BaseId)\
|
||||
class K##BaseId;\
|
||||
void intrusive_ptr_add_ref(K##BaseId *obj);\
|
||||
void intrusive_ptr_release(K##BaseId *obj);
|
||||
|
||||
|
||||
#define MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(BaseId)\
|
||||
inline void intrusive_ptr_add_ref(K##BaseId *obj) { intrusive_ptr_add_ref((KAutoObject *)obj); }\
|
||||
inline void intrusive_ptr_release(K##BaseId *obj) { intrusive_ptr_release((KAutoObject *)obj); }
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Process);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ResourceLimit);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Thread);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Event);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ReadableEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(WritableEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(InterruptEvent);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightClientSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(LightServerSession);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Port);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ClientPort);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(ServerPort);
|
||||
MESOSPHERE_AUTO_OBJECT_FW_DECL(Debug);
|
||||
|
||||
class KAutoObject {
|
||||
public:
|
||||
|
||||
/// Class token for polymorphic type checking
|
||||
virtual ushort GetClassToken() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Comparison key for KObjectAllocator
|
||||
virtual u64 GetComparisonKey() const
|
||||
{
|
||||
return (u64)(uiptr)this;
|
||||
}
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~KAutoObject();
|
||||
|
||||
|
||||
/// Check if the offset is base class of T or T itself
|
||||
template<typename T>
|
||||
bool IsInstanceOf() const
|
||||
{
|
||||
ushort btoken = GenerateClassToken<T>();
|
||||
ushort dtoken = GetClassToken();
|
||||
|
||||
return (dtoken & btoken) == btoken;
|
||||
}
|
||||
|
||||
// Explicitely disable copy and move, and add default ctor
|
||||
KAutoObject() = default;
|
||||
KAutoObject(const KAutoObject &) = delete;
|
||||
KAutoObject(KAutoObject &&) = delete;
|
||||
KAutoObject &operator=(const KAutoObject &) = delete;
|
||||
KAutoObject &operator=(KAutoObject &&) = delete;
|
||||
|
||||
/// Type order as found in official kernel
|
||||
enum class TypeId : ushort {
|
||||
AutoObject = 0,
|
||||
SynchronizationObject,
|
||||
ReadableEvent,
|
||||
|
||||
FinalClassesMin = 3,
|
||||
|
||||
InterruptEvent = 3,
|
||||
Debug,
|
||||
ClientSession,
|
||||
Thread,
|
||||
Process,
|
||||
Session,
|
||||
ServerPort,
|
||||
ResourceLimit,
|
||||
SharedMemory,
|
||||
LightClientSession,
|
||||
ServerSession,
|
||||
LightSession,
|
||||
Event,
|
||||
LightServerSession,
|
||||
DeviceAddressSpace,
|
||||
ClientPort,
|
||||
Port,
|
||||
WritableEvent,
|
||||
TransferMemory,
|
||||
SessionRequest,
|
||||
CodeMemory, // JIT
|
||||
|
||||
Max = CodeMemory + 1,
|
||||
};
|
||||
|
||||
private:
|
||||
std::atomic<ulong> referenceCount{0}; // official kernel has u32 for this
|
||||
friend void intrusive_ptr_add_ref(KAutoObject *obj);
|
||||
friend void intrusive_ptr_release(KAutoObject *obj);
|
||||
|
||||
protected:
|
||||
|
||||
template<typename T>
|
||||
static constexpr ushort GenerateClassToken()
|
||||
{
|
||||
/* The token follows these following properties:
|
||||
* Multiple inheritance is not supported
|
||||
* (BaseToken & DerivedToken) == BaseToken
|
||||
* The token for KAutoObject is 0
|
||||
* Not-final classes have a token of (1 << (typeid - 1))
|
||||
* Final derived classes have a unique token part of Seq[typeid - DerivedClassMin] | 0x100,
|
||||
where Seq is (in base 2) 11, 101, 110, 1001, 1010, and so on...
|
||||
*/
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
return 0;
|
||||
} else if constexpr (!std::is_final_v<T>) {
|
||||
static_assert(T::typeId >= TypeId::SynchronizationObject && T::typeId < TypeId::FinalClassesMin, "Invalid type ID!");
|
||||
return (1 << ((ushort)T::typeId - 1)) | GenerateClassToken<typename T::BaseClass>();
|
||||
} else {
|
||||
static_assert(T::typeId >= TypeId::FinalClassesMin && T::typeId < TypeId::Max, "Invalid type ID!");
|
||||
ushort off = (ushort)T::typeId - (ushort)TypeId::FinalClassesMin;
|
||||
return ((ushort)detail::A038444(off) << 9) | 0x100u | GenerateClassToken<typename T::BaseClass>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline void intrusive_ptr_add_ref(KAutoObject *obj)
|
||||
{
|
||||
ulong oldval = obj->referenceCount.fetch_add(1);
|
||||
kassert(oldval + 1 != 0);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(KAutoObject *obj)
|
||||
{
|
||||
ulong oldval = obj->referenceCount.fetch_sub(1);
|
||||
if (oldval - 1 == 0) {
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline SharedPtr<T> DynamicObjectCast(SharedPtr<KAutoObject> object) {
|
||||
if (object != nullptr && object->IsInstanceOf<T>()) {
|
||||
return boost::static_pointer_cast<T>(object);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
37
mesosphere/include/mesosphere/core/KCoreContext.hpp
Normal file
37
mesosphere/include/mesosphere/core/KCoreContext.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/arch/arch.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KProcess;
|
||||
class KThread;
|
||||
class KScheduler;
|
||||
class KAlarm;
|
||||
|
||||
class KCoreContext {
|
||||
public:
|
||||
static KCoreContext &GetInstance(uint coreId) { return instances[coreId]; };
|
||||
static KCoreContext &GetCurrentInstance() { return GetCurrentCoreContextInstance(); };
|
||||
|
||||
KThread *GetCurrentThread() const { return currentThread; }
|
||||
KProcess *GetCurrentProcess() const { return currentProcess; }
|
||||
KScheduler *GetScheduler() const { return scheduler; }
|
||||
KAlarm *GetAlarm() const { return alarm; }
|
||||
|
||||
KCoreContext(KScheduler *scheduler) : scheduler(scheduler) {}
|
||||
private:
|
||||
KThread *volatile currentThread = nullptr;
|
||||
KProcess *volatile currentProcess = nullptr;
|
||||
KScheduler *volatile scheduler = nullptr;
|
||||
KAlarm *volatile alarm = nullptr;
|
||||
|
||||
// more stuff
|
||||
|
||||
static std::array<KCoreContext, MAX_CORES> instances;
|
||||
};
|
||||
|
||||
}
|
||||
708
mesosphere/include/mesosphere/core/KLinkedList.hpp
Normal file
708
mesosphere/include/mesosphere/core/KLinkedList.hpp
Normal file
@@ -0,0 +1,708 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/interfaces/ISlabAllocated.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KLinkedList final {
|
||||
private:
|
||||
|
||||
|
||||
struct List final {
|
||||
struct Node final : public ISlabAllocated<Node> {
|
||||
|
||||
struct Link final {
|
||||
Link *prev = nullptr;
|
||||
Link *next = nullptr;
|
||||
|
||||
Node &parent()
|
||||
{
|
||||
return *detail::GetParentFromMember(this, &Node::link);
|
||||
}
|
||||
|
||||
const Node &parent() const
|
||||
{
|
||||
return *detail::GetParentFromMember(this, &Node::link);
|
||||
}
|
||||
|
||||
T &data() { return parent().data; }
|
||||
const T &data() const { return parent().data; }
|
||||
};
|
||||
|
||||
Link link{};
|
||||
T data{};
|
||||
|
||||
Node() = default;
|
||||
Node(const Node &other) = default;
|
||||
Node(Node &&other) = default;
|
||||
explicit Node(const T &data) : ISlabAllocated<Node>(), data{data} {}
|
||||
explicit Node(const T &&data) : ISlabAllocated<Node>(), data{data} {}
|
||||
template<typename ...Args>
|
||||
explicit Node(Args&& ...args) : ISlabAllocated<Node>(), data{std::forward<Args>(args)...} {}
|
||||
};
|
||||
|
||||
mutable typename Node::Link link{};
|
||||
typename Node::Link *last() const { return link.prev; }
|
||||
typename Node::Link *first() const { return link.next; }
|
||||
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
List list;
|
||||
|
||||
void insert_node_after(typename List::Node::Link *pos, typename List::Node::Link *nd)
|
||||
{
|
||||
// if pos is last & list is empty, ->next writes to first, etc.
|
||||
pos->next->prev = nd;
|
||||
nd->prev = pos;
|
||||
nd->next = pos->next;
|
||||
pos->next = nd;
|
||||
++list.size;
|
||||
}
|
||||
|
||||
void insert_node_before(typename List::Node::Link *pos, typename List::Node::Link *nd)
|
||||
{
|
||||
pos->prev->next = nd;
|
||||
nd->prev = pos->prev;
|
||||
nd->next = pos;
|
||||
pos->prev = nd;
|
||||
++list.size;
|
||||
}
|
||||
|
||||
void remove_node(typename List::Node::Link *nd)
|
||||
{
|
||||
nd->prev->next = nd->next;
|
||||
nd->next->prev = nd->prev;
|
||||
--list.size;
|
||||
}
|
||||
public:
|
||||
|
||||
template<bool isConst>
|
||||
class Iterator {
|
||||
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<
|
||||
isConst,
|
||||
typename KLinkedList::const_pointer,
|
||||
typename KLinkedList::pointer
|
||||
>::type;
|
||||
using reference = typename std::conditional<
|
||||
isConst,
|
||||
typename KLinkedList::const_reference,
|
||||
typename KLinkedList::reference
|
||||
>::type;
|
||||
|
||||
bool operator==(const Iterator &other) const
|
||||
{
|
||||
return node == other.node;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference operator*()
|
||||
{
|
||||
return node->data();
|
||||
}
|
||||
|
||||
pointer operator->()
|
||||
{
|
||||
return &node->data();
|
||||
}
|
||||
|
||||
Iterator &operator++()
|
||||
{
|
||||
node = node->next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator &operator--() {
|
||||
node = node->prev;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator &operator++(int) {
|
||||
const Iterator v{*this};
|
||||
++(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
Iterator &operator--(int) {
|
||||
const Iterator v{*this};
|
||||
--(*this);
|
||||
return v;
|
||||
}
|
||||
|
||||
// allow implicit const->non-const
|
||||
Iterator(const Iterator<false> &other) : node{other.node} {}
|
||||
|
||||
friend class Iterator<true>;
|
||||
Iterator() = default;
|
||||
|
||||
private:
|
||||
friend class KLinkedList;
|
||||
|
||||
typename KLinkedList::List::Node::Link *node;
|
||||
|
||||
explicit Iterator(typename KLinkedList::List::Node::Link *node) : node{node} {}
|
||||
};
|
||||
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using reference = T &;
|
||||
using const_reference = const T &;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
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>;
|
||||
|
||||
KLinkedList() : list{{&list.link, &list.link}} {};
|
||||
explicit KLinkedList(size_t count, const T &value) : KLinkedList()
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
explicit KLinkedList(size_t count) : KLinkedList()
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto *nd = new typename List::Node;
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
KLinkedList(InputIt first, InputIt last) : KLinkedList()
|
||||
{
|
||||
for (InputIt it = first; it != last; ++it) {
|
||||
auto *nd = new typename List::Node{*it};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(KLinkedList &other) noexcept
|
||||
{
|
||||
using std::swap; // Enable ADL
|
||||
swap(list.link, other.list.link);
|
||||
swap(list.size, other.list.size);
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
other.list.first()->prev = other.list.last()->next = &other.list.link;
|
||||
}
|
||||
|
||||
KLinkedList(KLinkedList &&other) noexcept : KLinkedList()
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
KLinkedList(std::initializer_list<T> ilist) : KLinkedList(ilist.begin(), ilist.end()) {}
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
typename List::Node::Link *nxt;
|
||||
for (typename List::Node::Link *nd = list.first(); nd != &list.link; nd = nxt) {
|
||||
nxt = nd->next;
|
||||
delete &nd->parent();
|
||||
}
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
~KLinkedList()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
KLinkedList &operator=(KLinkedList other)
|
||||
{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
KLinkedList &operator=(std::initializer_list<T> ilist)
|
||||
{
|
||||
KLinkedList tmp{ilist};
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void assign(size_t count, const T &value)
|
||||
{
|
||||
KLinkedList tmp{count, value};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
void assign(InputIt first, InputIt last)
|
||||
{
|
||||
KLinkedList tmp{first, last};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
void assign(std::initializer_list<T> ilist)
|
||||
{
|
||||
KLinkedList tmp{ilist};
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
T &front() { return list.first()->data(); }
|
||||
const T &front() const { return list.first()->data(); }
|
||||
|
||||
T &back() { return list.last()->data(); }
|
||||
const T &back() const { return list.last()->data(); }
|
||||
|
||||
const_iterator cbegin() const noexcept { return const_iterator{list.first()}; }
|
||||
const_iterator cend() const noexcept { return const_iterator{&list.link}; }
|
||||
|
||||
const_iterator begin() const noexcept { return cbegin(); }
|
||||
const_iterator end() const noexcept { return cend(); }
|
||||
|
||||
iterator begin() noexcept { return iterator{list.first()}; }
|
||||
iterator end() noexcept { return iterator{&list.link}; }
|
||||
|
||||
const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{list.last()}; }
|
||||
const_reverse_iterator crend() const noexcept { return const_reverse_iterator{&list.link}; }
|
||||
|
||||
const_reverse_iterator rbegin() const noexcept { return crbegin(); }
|
||||
const_reverse_iterator rend() const noexcept { return crend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return reverse_iterator{list.last()}; }
|
||||
reverse_iterator rend() noexcept { return reverse_iterator{&list.link}; }
|
||||
|
||||
KLinkedList(const KLinkedList &other) : KLinkedList(other.cbegin(), other.cend()) {}
|
||||
|
||||
constexpr size_t size() const noexcept { return list.size; }
|
||||
constexpr bool empty() const noexcept { return list.size == 0; }
|
||||
|
||||
iterator insert(const_iterator pos, const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(pos.node, &nd->link);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(pos.node, &nd->link);
|
||||
return iterator{&nd->link};
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, size_t count, const T &value)
|
||||
{
|
||||
iterator ret = pos;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ret = insert(ret, value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename InputIt, typename = std::enable_if_t<!std::is_integral_v<InputIt>>>
|
||||
iterator insert(const_iterator pos, InputIt first, InputIt last)
|
||||
{
|
||||
// Note: our list definition allows --begin() to be well defined
|
||||
typename List::Node::Link *f = nullptr;
|
||||
typename List::Node::Link *p = pos.node->prev;
|
||||
for(InputIt it = first; it != last; ++it) {
|
||||
auto *nd = new typename List::Node{*it};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(p, &nd->link);
|
||||
p = &nd->link;
|
||||
f = f == nullptr ? p : f;
|
||||
}
|
||||
|
||||
return iterator{f};
|
||||
}
|
||||
|
||||
iterator insert(const_iterator pos, std::initializer_list<T> ilist)
|
||||
{
|
||||
return insert(pos, ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
iterator emplace(const_iterator pos, Args &&...args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(pos.node, &nd->link);
|
||||
return iterator{&nd->link};
|
||||
}
|
||||
|
||||
iterator erase(const_iterator pos)
|
||||
{
|
||||
iterator ret{pos.node->next};
|
||||
remove_node(pos.node);
|
||||
delete &pos.node->parent();
|
||||
return ret;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
const_iterator next;
|
||||
for (const_iterator it = first; it != last; it = next) {
|
||||
next = erase(it);
|
||||
}
|
||||
return iterator{next.node};
|
||||
}
|
||||
|
||||
void push_back(const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
|
||||
void push_back(T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T &emplace_back(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
return nd->data;
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T *emplace_back_or_fail(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
if (nd != nullptr) {
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
return &nd->data;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
auto *nd = list.last();
|
||||
remove_node(nd);
|
||||
delete &nd->parent();
|
||||
}
|
||||
|
||||
void push_front(const T &value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
}
|
||||
|
||||
void push_front(T &&value)
|
||||
{
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
}
|
||||
|
||||
template< class... Args>
|
||||
T &emplace_front(Args&&... args)
|
||||
{
|
||||
auto *nd = new typename List::Node{std::forward<Args>(args)...};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_before(list.first(), &nd->link);
|
||||
return nd->data;
|
||||
}
|
||||
|
||||
void pop_front()
|
||||
{
|
||||
auto *nd = list.first();
|
||||
remove_node(nd);
|
||||
delete &nd->parent();
|
||||
}
|
||||
|
||||
void resize(size_t count)
|
||||
{
|
||||
if (count < list.size) {
|
||||
while (count < list.size) {
|
||||
pop_back();
|
||||
}
|
||||
} else {
|
||||
size_t s = list.size;
|
||||
for (size_t i = 0; i < count - s; i++) {
|
||||
auto *nd = new typename List::Node;
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t count, const T &value)
|
||||
{
|
||||
if (count < list.size) {
|
||||
while (count < list.size) {
|
||||
pop_back();
|
||||
}
|
||||
} else {
|
||||
size_t s = list.size;
|
||||
for (size_t i = 0; i < count - s; i++) {
|
||||
auto *nd = new typename List::Node{value};
|
||||
kassert(nd != nullptr);
|
||||
insert_node_after(list.last(), &nd->link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other)
|
||||
{
|
||||
//auto *current = pos.node;
|
||||
auto *before = pos.node->prev;
|
||||
auto *after = pos.node; //current == &list.link ? current : pos.node->next;
|
||||
before->next = other.list.first();
|
||||
before->next->prev = before;
|
||||
after->prev = other.list.last();
|
||||
after->prev->next = after;
|
||||
|
||||
list.size += other.list.size;
|
||||
other.list.size = 0;
|
||||
other.list.link.prev = other.list.link.next = &other.list.link;
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other)
|
||||
{
|
||||
splice(pos, std::move(other));
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other, const_iterator it)
|
||||
{
|
||||
other.remove_node(it.node);
|
||||
insert_node_before(pos.node, it.node);
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other, const_iterator it)
|
||||
{
|
||||
splice(pos, std::move(other), it);
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &&other, const_iterator first, const_iterator last)
|
||||
{
|
||||
if (*this == other) {
|
||||
auto *current = pos.node;
|
||||
auto *before = pos.node->prev;
|
||||
auto *after = current == &list.link ? current : pos.node->next;
|
||||
before->next = first.node;
|
||||
before->next->prev = before;
|
||||
after->prev = last.node;
|
||||
after->prev->next = after;
|
||||
} else {
|
||||
// Note: the first, last version can't be O(1) because std::distance is O(n)...
|
||||
const_iterator next;
|
||||
for (const_iterator it = first; it != last; it = next) {
|
||||
next = it;
|
||||
++next;
|
||||
splice(pos, other, it);
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void splice(const_iterator pos, KLinkedList &other, const_iterator first, const_iterator last)
|
||||
{
|
||||
splice(pos, std::move(other), first, last);
|
||||
}
|
||||
|
||||
size_t remove(const T &value)
|
||||
{
|
||||
size_t n = 0;
|
||||
const_iterator next;
|
||||
for (const_iterator it = cbegin(); it != cend(); ) {
|
||||
if (*it == value) {
|
||||
it = erase(it);
|
||||
++n;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename UnaryPredicate>
|
||||
size_t remove_if(UnaryPredicate p)
|
||||
{
|
||||
const_iterator next;
|
||||
size_t n = 0;
|
||||
for (const_iterator it = cbegin(); it != cend(); ) {
|
||||
if (p(*it)) {
|
||||
it = erase(it);
|
||||
++n;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename Compare>
|
||||
void merge(KLinkedList &&other, Compare p)
|
||||
{
|
||||
typename List::Node::Link hd{};
|
||||
auto *cur = &hd;
|
||||
size_t n = 0;
|
||||
while (list.size > 0 && other.list.size > 0) {
|
||||
if (p(list.first()->data(), other.list.first()->data())) {
|
||||
cur->next = list.first();
|
||||
remove_node(list.first());
|
||||
} else {
|
||||
cur->next = other.list.first();
|
||||
other.remove_node(other.list.first());
|
||||
}
|
||||
cur->next->prev = cur;
|
||||
cur = cur->next;
|
||||
n++;
|
||||
}
|
||||
|
||||
// Steal the remaining elements
|
||||
if (list.size > 0) {
|
||||
cur->next = list.first();
|
||||
list.first()->prev = cur;
|
||||
cur = list.last();
|
||||
n += list.size;
|
||||
} else if (other.list.size > 0) {
|
||||
cur->next = other.list.first();
|
||||
other.list.first()->prev = cur;
|
||||
cur = other.list.last();
|
||||
n += other.list.size;
|
||||
}
|
||||
|
||||
|
||||
// Reset the other list to put it in a valid state
|
||||
other.list.link.prev = other.list.link.next = &other.list.link;
|
||||
other.list.size = 0;
|
||||
|
||||
// Finally, normalize the result and assign it to this
|
||||
list.link.next = hd.next;
|
||||
list.link.prev = cur;
|
||||
list.size = n;
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
}
|
||||
|
||||
void merge(KLinkedList &&other)
|
||||
{
|
||||
merge(other, std::less<T>{});
|
||||
}
|
||||
|
||||
template<typename Compare>
|
||||
void merge(KLinkedList &other, Compare p)
|
||||
{
|
||||
merge(std::move(other), p);
|
||||
}
|
||||
|
||||
void merge(KLinkedList &other)
|
||||
{
|
||||
merge(other, std::less<T>{});
|
||||
}
|
||||
|
||||
void reverse() noexcept
|
||||
{
|
||||
typename List::Node::Link hd{};
|
||||
auto *cur = &hd;
|
||||
size_t n = 0;
|
||||
|
||||
while (list.size > 0) {
|
||||
cur->next = list.last();
|
||||
remove_node(list.last());
|
||||
cur->next->prev = cur;
|
||||
n++;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
list.link.next = hd.next;
|
||||
list.link.prev = cur;
|
||||
list.size = n;
|
||||
list.first()->prev = list.last()->next = &list.link;
|
||||
}
|
||||
|
||||
|
||||
template<typename BinaryPredicate>
|
||||
size_t unique(BinaryPredicate p)
|
||||
{
|
||||
typename List::Node::Link *nxt;
|
||||
size_t n = 0;
|
||||
for (auto *nd = list.first(); nd != &list.link; nd = nxt) {
|
||||
nxt = nd->next;
|
||||
for (auto *nd2 = nxt; nd2 != &list.link && p(nd->data(), nd2->data()); nd2 = nxt) {
|
||||
nxt = nd2->next;
|
||||
remove_node(nd2);
|
||||
delete &nd2->parent();
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t unique()
|
||||
{
|
||||
return unique(std::equal_to<T>{});
|
||||
}
|
||||
|
||||
// sort: a PITA to implement and not needed anyway
|
||||
|
||||
friend bool operator==(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
|
||||
}
|
||||
|
||||
friend bool operator!=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend bool operator<(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less<T>{});
|
||||
}
|
||||
|
||||
friend bool operator>(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater<T>{});
|
||||
}
|
||||
|
||||
friend bool operator<=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::less_equal<T>{});
|
||||
}
|
||||
|
||||
friend bool operator>=(const KLinkedList &lhs, const KLinkedList &rhs)
|
||||
{
|
||||
return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), std::greater_equal<T>{});
|
||||
}
|
||||
|
||||
friend void swap(KLinkedList &lhs, KLinkedList &rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename InputIt>
|
||||
KLinkedList(InputIt b, InputIt e) -> KLinkedList<typename std::iterator_traits<InputIt>::value_type>;
|
||||
|
||||
}
|
||||
43
mesosphere/include/mesosphere/core/KObjectRegistry.hpp
Normal file
43
mesosphere/include/mesosphere/core/KObjectRegistry.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/core/KLinkedList.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KObjectRegistry {
|
||||
public:
|
||||
|
||||
static KObjectRegistry &GetInstance() { return instance; }
|
||||
|
||||
SharedPtr<KAutoObject> Find(const char *name) const;
|
||||
Result Register(SharedPtr<KAutoObject> obj, const char *name);
|
||||
Result Unregister(const char *name);
|
||||
|
||||
private:
|
||||
|
||||
struct Node {
|
||||
SharedPtr<KAutoObject> obj{};
|
||||
char name[12] = {0};
|
||||
|
||||
Node() = default;
|
||||
Node(SharedPtr<KAutoObject> &&obj, const char *name) : obj{obj}
|
||||
{
|
||||
std::strncpy(this->name, name, sizeof(this->name));
|
||||
}
|
||||
};
|
||||
|
||||
const Node *FindImpl(const char *name) const;
|
||||
Node *FindImpl(const char *name);
|
||||
|
||||
KLinkedList<Node> nameNodes{};
|
||||
mutable KMutex mutex{};
|
||||
|
||||
static KObjectRegistry instance;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/core/KLinkedList.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KSynchronizationObject : public KAutoObject {
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, SynchronizationObject);
|
||||
|
||||
virtual ~KSynchronizationObject();
|
||||
virtual bool IsSignaled() const = 0;
|
||||
|
||||
void NotifyWaiters(); // Note: NotifyWaiters() with !IsSignaled() is no-op
|
||||
|
||||
KLinkedList<KThread *>::const_iterator AddWaiter(KThread &thread);
|
||||
void RemoveWaiter(KLinkedList<KThread *>::const_iterator it);
|
||||
|
||||
private:
|
||||
KLinkedList<KThread *> waiters{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(SynchronizationObject);
|
||||
|
||||
}
|
||||
143
mesosphere/include/mesosphere/core/Result.hpp
Normal file
143
mesosphere/include/mesosphere/core/Result.hpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
enum class ResultModule : uint {
|
||||
None = 0,
|
||||
Kernel = 1,
|
||||
/* Other modules not included. */
|
||||
};
|
||||
|
||||
class ResultHelper {
|
||||
public:
|
||||
using BaseType = uint;
|
||||
static constexpr BaseType SuccessValue = BaseType();
|
||||
static constexpr BaseType ModuleBits = 9;
|
||||
static constexpr BaseType DescriptionBits = 13;
|
||||
|
||||
template<ResultModule module, BaseType description>
|
||||
struct MakeResult : public std::integral_constant<BaseType, ((static_cast<BaseType>(module)) | (description << ModuleBits))> {
|
||||
static_assert(static_cast<BaseType>(module) < 1 << (ModuleBits + 1), "Invalid Module");
|
||||
static_assert(description < 1 << (DescriptionBits + 1), "Invalid Description");
|
||||
};
|
||||
|
||||
static constexpr ResultModule GetModule(BaseType value) {
|
||||
return static_cast<ResultModule>(value & ~(~BaseType() << ModuleBits));
|
||||
}
|
||||
|
||||
static constexpr BaseType GetDescription(BaseType value) {
|
||||
return ((value >> ModuleBits) & ~(~BaseType() << DescriptionBits));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Use CRTP for Results. */
|
||||
template<typename Self>
|
||||
class ResultBase {
|
||||
public:
|
||||
using BaseType = typename ResultHelper::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultHelper::SuccessValue;
|
||||
|
||||
constexpr bool IsSuccess() const { return static_cast<const Self *>(this)->GetValue() == SuccessValue; }
|
||||
constexpr bool IsFailure() const { return !IsSuccess(); }
|
||||
constexpr operator bool() const { return IsSuccess(); }
|
||||
constexpr bool operator !() const { return IsFailure(); }
|
||||
|
||||
constexpr ResultModule GetModule() const { return static_cast<const Self *>(this)->GetValue(); }
|
||||
constexpr BaseType GetDescription() const { return static_cast<const Self *>(this)->GetValue(); }
|
||||
};
|
||||
|
||||
/* Actual result type. */
|
||||
class Result final : public ResultBase<Result> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<Result>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<Result>::SuccessValue;
|
||||
|
||||
constexpr Result() : value(SuccessValue) {}
|
||||
|
||||
constexpr BaseType GetValue() const { return this->value; }
|
||||
constexpr bool operator==(const Result &other) const { return value == other.value; }
|
||||
constexpr bool operator!=(const Result &other) const { return value != other.value; }
|
||||
|
||||
static constexpr Result MakeResult(BaseType v) { return Result(v); }
|
||||
|
||||
private:
|
||||
BaseType value;
|
||||
constexpr explicit Result(BaseType v) : value(v) {}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Result) == sizeof(Result::BaseType), "Bad Result definition!");
|
||||
|
||||
/* Successful result class. */
|
||||
class ResultSuccess final : public ResultBase<ResultSuccess> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<ResultSuccess>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<ResultSuccess>::SuccessValue;
|
||||
|
||||
constexpr operator Result() { return Result::MakeResult(SuccessValue); }
|
||||
constexpr BaseType GetValue() const { return SuccessValue; }
|
||||
};
|
||||
|
||||
/* Error result class. */
|
||||
template<ResultModule module, ResultHelper::BaseType description>
|
||||
class ResultError : public ResultBase<ResultError<module, description>> {
|
||||
public:
|
||||
using BaseType = typename ResultBase<ResultError<module, description>>::BaseType;
|
||||
static constexpr BaseType SuccessValue = ResultBase<ResultError<module, description>>::SuccessValue;
|
||||
|
||||
static constexpr BaseType Value = ResultHelper::MakeResult<module, description>::value;
|
||||
static_assert(Value != SuccessValue, "Invalid ResultError");
|
||||
|
||||
constexpr operator Result() { return Result::MakeResult(Value); }
|
||||
constexpr BaseType GetValue() const { return Value; }
|
||||
};
|
||||
|
||||
#define DEFINE_RESULT(module, name, description) class Result##module##name final : public ResultError<ResultModule::module, description> {}
|
||||
|
||||
DEFINE_RESULT(Kernel, OutOfSessions, 7);
|
||||
|
||||
DEFINE_RESULT(Kernel, InvalidCapabilityDescriptor, 14);
|
||||
|
||||
DEFINE_RESULT(Kernel, NotImplemented, 33);
|
||||
DEFINE_RESULT(Kernel, ThreadTerminating, 59);
|
||||
|
||||
DEFINE_RESULT(Kernel, OutOfDebugEvents, 70);
|
||||
|
||||
DEFINE_RESULT(Kernel, InvalidSize, 101);
|
||||
DEFINE_RESULT(Kernel, InvalidAddress, 102);
|
||||
DEFINE_RESULT(Kernel, ResourceExhausted, 103);
|
||||
DEFINE_RESULT(Kernel, OutOfMemory, 104);
|
||||
DEFINE_RESULT(Kernel, OutOfHandles, 105);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryState, 106);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryPermissions, 108);
|
||||
DEFINE_RESULT(Kernel, InvalidMemoryRange, 110);
|
||||
DEFINE_RESULT(Kernel, InvalidPriority, 112);
|
||||
DEFINE_RESULT(Kernel, InvalidCoreId, 113);
|
||||
DEFINE_RESULT(Kernel, InvalidHandle, 114);
|
||||
DEFINE_RESULT(Kernel, InvalidUserBuffer, 115);
|
||||
DEFINE_RESULT(Kernel, InvalidCombination, 116);
|
||||
DEFINE_RESULT(Kernel, TimedOut, 117);
|
||||
DEFINE_RESULT(Kernel, Cancelled, 118);
|
||||
DEFINE_RESULT(Kernel, OutOfRange, 119);
|
||||
DEFINE_RESULT(Kernel, InvalidEnumValue, 120);
|
||||
DEFINE_RESULT(Kernel, NotFound, 121);
|
||||
DEFINE_RESULT(Kernel, AlreadyExists, 122);
|
||||
DEFINE_RESULT(Kernel, ConnectionClosed, 123);
|
||||
DEFINE_RESULT(Kernel, UnhandledUserInterrupt, 124);
|
||||
DEFINE_RESULT(Kernel, InvalidState, 125);
|
||||
DEFINE_RESULT(Kernel, ReservedValue, 126);
|
||||
DEFINE_RESULT(Kernel, InvalidHwBreakpoint, 127);
|
||||
DEFINE_RESULT(Kernel, FatalUserException, 128);
|
||||
DEFINE_RESULT(Kernel, OwnedByAnotherProcess, 129);
|
||||
DEFINE_RESULT(Kernel, ConnectionRefused, 131);
|
||||
DEFINE_RESULT(Kernel, OutOfResource, 132);
|
||||
|
||||
DEFINE_RESULT(Kernel, IpcMapFailed, 259);
|
||||
DEFINE_RESULT(Kernel, IpcCmdbufTooSmall, 260);
|
||||
|
||||
DEFINE_RESULT(Kernel, NotDebugged, 520);
|
||||
|
||||
}
|
||||
118
mesosphere/include/mesosphere/core/make_object.hpp
Normal file
118
mesosphere/include/mesosphere/core/make_object.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/processes/KProcess.hpp>
|
||||
#include <mesosphere/kresources/KResourceLimit.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
auto MakeObjectRaw(Args&& ...args)
|
||||
{
|
||||
Result res = ResultSuccess();
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
auto reslimit = cctx.GetCurrentProcess()->GetResourceLimit();
|
||||
bool doReslimitCleanup = false;
|
||||
T *obj = nullptr;
|
||||
if constexpr (std::is_base_of_v<ILimitedResource<T>, T>) {
|
||||
if (reslimit != nullptr) {
|
||||
if (reslimit->Reserve(KResourceLimit::categoryOf<T>, 1, T::maxResourceAcqWaitTime)) {
|
||||
doReslimitCleanup = true;
|
||||
} else {
|
||||
return std::tuple<Result, T *>{ResultKernelOutOfResource(), nullptr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj = new T;
|
||||
if (obj == nullptr) {
|
||||
res = ResultKernelResourceExhausted();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = obj->Initialize(std::forward<Args>(args)...);
|
||||
if (res.IsSuccess()) {
|
||||
doReslimitCleanup = false;
|
||||
if constexpr (std::is_base_of_v<ISetAllocated<T>, T>) {
|
||||
obj->AddToAllocatedSet();
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_base_of_v<IClientServerParentTag, T>) {
|
||||
delete &obj->GetClient();
|
||||
delete &obj->GetServer();
|
||||
} else {
|
||||
delete obj;
|
||||
}
|
||||
|
||||
obj = nullptr;
|
||||
}
|
||||
cleanup:
|
||||
if (doReslimitCleanup) {
|
||||
reslimit->Release(KResourceLimit::categoryOf<T>, 1, 1);
|
||||
}
|
||||
|
||||
return std::tuple{res, obj};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<!std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObject(Args&& ...args)
|
||||
{
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
return std::tuple<Result, SharedPtr<T>>{res, SharedPtr<T>{obj}};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObject(Args&& ...args)
|
||||
{
|
||||
// Bug in type inference?
|
||||
using RetType = std::tuple<Result, SharedPtr<typename T::ServerClass>, SharedPtr<typename T::ClientClass>>;
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
return res.IsSuccess() ? RetType{res, &obj->GetServer(), &obj->GetClient()} : RetType{res, nullptr, nullptr};
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<!std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObjectWithHandle(Args&& ...args)
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
KProcess *currentProcess = cctx.GetCurrentProcess();
|
||||
KHandleTable &tbl = currentProcess->GetHandleTable();
|
||||
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
if (res.IsFailure()) {
|
||||
return std::tuple{res, Handle{}};
|
||||
}
|
||||
|
||||
return tbl.Generate(obj);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if_t<std::is_base_of_v<IClientServerParentTag, T>> * = nullptr, typename ...Args>
|
||||
auto MakeObjectWithHandle(Args&& ...args)
|
||||
{
|
||||
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
|
||||
KProcess *currentProcess = cctx.GetCurrentProcess();
|
||||
KHandleTable &tbl = currentProcess->GetHandleTable();
|
||||
|
||||
auto [res, obj] = MakeObjectRaw<T>(std::forward<Args>(args)...);
|
||||
if (res.IsFailure()) {
|
||||
return std::tuple{res, Handle{}, Handle{}};
|
||||
}
|
||||
|
||||
auto [res2, serverHandle] = tbl.Generate(&obj->GetServer());
|
||||
if (res2.IsSuccess()) {
|
||||
auto [res3, clientHandle] = tbl.Generate(&obj->GetClient());
|
||||
if (res3.IsSuccess()) {
|
||||
return std::tuple{res3, serverHandle, clientHandle};
|
||||
} else {
|
||||
tbl.Close(serverHandle);
|
||||
return std::tuple{res3, Handle{}, Handle{}};
|
||||
}
|
||||
} else {
|
||||
return std::tuple{res2, Handle{}, Handle{}};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
48
mesosphere/include/mesosphere/core/types.hpp
Normal file
48
mesosphere/include/mesosphere/core/types.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
#include <chrono>
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#define MAX_CORES 4
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using ushort = unsigned short;
|
||||
using uint = unsigned int;
|
||||
using ulong = unsigned long;
|
||||
|
||||
using std::size_t;
|
||||
|
||||
using uiptr = std::uintptr_t;
|
||||
using iptr = std::intptr_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
using s8 = int8_t;
|
||||
using s16 = int16_t;
|
||||
using s32 = int32_t;
|
||||
using s64 = int64_t;
|
||||
|
||||
using vu8 = volatile uint8_t;
|
||||
using vu16 = volatile uint16_t;
|
||||
using vu32 = volatile uint32_t;
|
||||
using vu64 = volatile uint64_t;
|
||||
|
||||
using vs8 = volatile int8_t;
|
||||
using vs16 = volatile int16_t;
|
||||
using vs32 = volatile int32_t;
|
||||
using vs64 = volatile int64_t;
|
||||
|
||||
template <typename T>
|
||||
using SharedPtr = boost::intrusive_ptr<T>;
|
||||
|
||||
}
|
||||
109
mesosphere/include/mesosphere/core/util.hpp
Normal file
109
mesosphere/include/mesosphere/core/util.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <array>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
/*
|
||||
Boost doesn't provide get_parent_from members for arrays so we have to implement this manually
|
||||
for arrays, for gcc at leadt.
|
||||
|
||||
Thanks fincs.
|
||||
*/
|
||||
|
||||
#define kassert(cond) ((void)(cond))
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename ClassT, typename MemberT>
|
||||
union __my_offsetof {
|
||||
const MemberT ClassT::* ptr;
|
||||
iptr offset;
|
||||
};
|
||||
|
||||
// Thanks neobrain
|
||||
template<typename T, size_t N, typename... Args, size_t... Indexes>
|
||||
static constexpr std::array<T, N> MakeArrayOfHelper(Args&&... args, std::index_sequence<Indexes...>) {
|
||||
// There are two parameter pack expansions here:
|
||||
// * The inner expansion is over "t"
|
||||
// * The outer expansion is over "Indexes"
|
||||
//
|
||||
// This function will always be called with sizeof...(Indexes) == N,
|
||||
// so the outer expansion generates exactly N copies of the constructor call
|
||||
return std::array<T, N> { ((void)Indexes, T { args... })... };
|
||||
}
|
||||
|
||||
// Thanks neobrain
|
||||
template<typename T, typename F, size_t N, typename... Args, size_t... Indexes>
|
||||
static constexpr std::array<T, N> MakeArrayWithFactorySequenceOfHelper(Args&&... args, std::index_sequence<Indexes...>) {
|
||||
return std::array<T, N> { T { F{}(std::integral_constant<size_t, Indexes>{}), args... }... };
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename ClassT, typename MemberT, size_t N>
|
||||
constexpr ClassT* GetParentFromArrayMember(MemberT* member, size_t index, const MemberT (ClassT::* ptr)[N]) noexcept {
|
||||
member -= index;
|
||||
return (ClassT*)((iptr)member - __my_offsetof<ClassT,MemberT[N]> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT, size_t N>
|
||||
constexpr const ClassT* GetParentFromArrayMember(const MemberT* member, size_t index, const MemberT (ClassT::* ptr)[N]) noexcept {
|
||||
member -= index;
|
||||
return (const ClassT*)((iptr)member - __my_offsetof<ClassT,MemberT[N]> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT>
|
||||
constexpr ClassT* GetParentFromMember(MemberT* member, const MemberT ClassT::* ptr) noexcept {
|
||||
return (ClassT*)((iptr)member - __my_offsetof<ClassT, MemberT> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename ClassT, typename MemberT>
|
||||
constexpr const ClassT* GetParentFromMember(const MemberT* member, const MemberT ClassT::* ptr) noexcept {
|
||||
return (const ClassT*)((iptr)member - __my_offsetof<ClassT, MemberT> { ptr }.offset);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename... Args>
|
||||
constexpr std::array<T, N> MakeArrayOf(Args&&... args) {
|
||||
return MakeArrayOfHelper<T, N, Args...>(std::forward<Args>(args)..., std::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
template<typename T, typename F, size_t N, typename... Args>
|
||||
constexpr std::array<T, N> MakeArrayWithFactorySequenceOf(Args&&... args) {
|
||||
return MakeArrayWithFactorySequenceOfHelper<T, F, N, Args...>(std::forward<Args>(args)..., std::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
/// Sequence of two distinc powers of 2
|
||||
constexpr ulong A038444(ulong n)
|
||||
{
|
||||
if (n == 0) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
ulong v = A038444(n - 1);
|
||||
ulong m1 = 1 << (63 - __builtin_clzl(v));
|
||||
ulong m2 = 1 << (63 - __builtin_clzl(v&~m1));
|
||||
|
||||
if (m2 << 1 == m1) {
|
||||
m2 = 1;
|
||||
m1 <<= 1;
|
||||
} else {
|
||||
m2 <<= 1;
|
||||
}
|
||||
|
||||
return m1 | m2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
61
mesosphere/include/mesosphere/interfaces/IAlarmable.hpp
Normal file
61
mesosphere/include/mesosphere/interfaces/IAlarmable.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <mesosphere/board/KSystemClock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct KAlarm;
|
||||
|
||||
struct AlarmableSetTag;
|
||||
|
||||
using AlarmableSetBaseHook = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::tag<AlarmableSetTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
|
||||
class IAlarmable : public AlarmableSetBaseHook {
|
||||
public:
|
||||
struct Comparator {
|
||||
constexpr bool operator()(const IAlarmable &lhs, const IAlarmable &rhs) const {
|
||||
return lhs.alarmTime < rhs.alarmTime;
|
||||
}
|
||||
};
|
||||
|
||||
virtual void OnAlarm() = 0;
|
||||
|
||||
constexpr KSystemClock::time_point GetAlarmTime() const { return alarmTime; }
|
||||
|
||||
/// Precondition: alarm has not been set
|
||||
template<typename Clock, typename Duration>
|
||||
void SetAlarmTime(const std::chrono::time_point<Clock, Duration> &alarmTime)
|
||||
{
|
||||
SetAlarmTimeImpl(alarmTime);
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
void SetAlarmIn(const std::chrono::duration<Rep, Period> &alarmTimeOffset)
|
||||
{
|
||||
SetAlarmTimeImpl(KSystemClock::now() + alarmTimeOffset);
|
||||
}
|
||||
|
||||
void ClearAlarm();
|
||||
|
||||
private:
|
||||
void SetAlarmTimeImpl(const KSystemClock::time_point &alarmTime);
|
||||
|
||||
KSystemClock::time_point alarmTime = KSystemClock::time_point{};
|
||||
|
||||
friend class KAlarm;
|
||||
};
|
||||
|
||||
|
||||
using AlarmableSetType =
|
||||
boost::intrusive::make_set<
|
||||
IAlarmable,
|
||||
boost::intrusive::base_hook<AlarmableSetBaseHook>,
|
||||
boost::intrusive::compare<IAlarmable::Comparator>
|
||||
>::type;
|
||||
|
||||
}
|
||||
31
mesosphere/include/mesosphere/interfaces/IClient.hpp
Normal file
31
mesosphere/include/mesosphere/interfaces/IClient.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
|
||||
#define MESOSPHERE_CLIENT_TRAITS(ParentId) using ParentClass = K##ParentId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IClientTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IClient : public IClientTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
void *operator new(size_t sz) noexcept = delete;
|
||||
void operator delete(void *ptr) noexcept {}
|
||||
|
||||
ParentClass *GetParent() const { return parent.get(); }
|
||||
|
||||
protected:
|
||||
friend class IClientServerParent<ParentClass, ClientClass, ServerClass>;
|
||||
SharedPtr<ParentClass> parent{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/types.hpp>
|
||||
|
||||
#define MESOSPHERE_CLIENT_SERVER_PARENT_TRAITS(ClientId, ServerId)\
|
||||
using ClientClass = K##ClientId;\
|
||||
using ServerClass = K##ServerId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IClientServerParentTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IClientServerParent : public IClientServerParentTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
ClientClass &GetClient() { return client; }
|
||||
ServerClass &GetServer() { return server; }
|
||||
|
||||
protected:
|
||||
|
||||
void SetClientServerParent()
|
||||
{
|
||||
ParentClass *par = (ParentClass *)this;
|
||||
client.parent = par;
|
||||
server.parent = par;
|
||||
}
|
||||
|
||||
ClientClass client{};
|
||||
ServerClass server{};
|
||||
};
|
||||
|
||||
}
|
||||
16
mesosphere/include/mesosphere/interfaces/IInterruptible.hpp
Normal file
16
mesosphere/include/mesosphere/interfaces/IInterruptible.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class IWork;
|
||||
|
||||
class IInterruptible {
|
||||
public:
|
||||
|
||||
/// Top half in Linux jargon
|
||||
virtual IWork *HandleInterrupt(uint interruptId) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IWork.hpp>
|
||||
#include <mesosphere/interfaces/IInterruptible.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class IInterruptibleWork : public IInterruptible, public IWork {
|
||||
public:
|
||||
|
||||
virtual IWork *HandleInterrupt(uint interruptId) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
// circular dep: #include "resource_limit.h"
|
||||
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <tuple>
|
||||
|
||||
#define MESOSPHERE_LIMITED_RESOURCE_TRAITS(maxTime) static constexpr auto maxResourceAcqWaitTime = maxTime;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void ReleaseResource(const SharedPtr<KProcess> &owner, KAutoObject::TypeId typeId, size_t count, size_t realCount);
|
||||
void ReleaseResource(const SharedPtr<KResourceLimit> &reslimit, KAutoObject::TypeId typeId, size_t count, size_t realCount);
|
||||
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
class ILimitedResource {
|
||||
public:
|
||||
|
||||
const SharedPtr<KProcess>& GetResourceOwner() const { return resourceOwner; }
|
||||
void SetResourceOwner(SharedPtr<KProcess> owner)
|
||||
{
|
||||
resourceOwner = std::move(owner);
|
||||
isLimitedResourceActive = true;
|
||||
}
|
||||
|
||||
virtual std::tuple<size_t, size_t> GetResourceCount()
|
||||
{
|
||||
return {1, 1}; // current, real
|
||||
}
|
||||
|
||||
~ILimitedResource()
|
||||
{
|
||||
if (isLimitedResourceActive) {
|
||||
auto [cur, real] = GetResourceCount();
|
||||
detail::ReleaseResource(resourceOwner, Derived::typeId, cur, real);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SharedPtr<KProcess> resourceOwner{};
|
||||
bool isLimitedResourceActive = false;
|
||||
};
|
||||
|
||||
}
|
||||
31
mesosphere/include/mesosphere/interfaces/IServer.hpp
Normal file
31
mesosphere/include/mesosphere/interfaces/IServer.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <mesosphere/core/types.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
|
||||
#define MESOSPHERE_SERVER_TRAITS(ParentId) using ParentClass = K##ParentId;
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct IServerTag {};
|
||||
|
||||
template<typename Parent, typename Client, typename Server>
|
||||
class IServer : public IServerTag {
|
||||
public:
|
||||
using ParentClass = Parent;
|
||||
using ClientClass = Client;
|
||||
using ServerClass = Server;
|
||||
|
||||
void *operator new(size_t sz) noexcept = delete;
|
||||
void operator delete(void *ptr) noexcept {};
|
||||
|
||||
ParentClass *GetParent() const { return parent.get(); }
|
||||
|
||||
protected:
|
||||
friend class IClientServerParent<ParentClass, ClientClass, ServerClass>;
|
||||
SharedPtr<ParentClass> parent{};
|
||||
};
|
||||
|
||||
}
|
||||
60
mesosphere/include/mesosphere/interfaces/ISetAllocated.hpp
Normal file
60
mesosphere/include/mesosphere/interfaces/ISetAllocated.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KObjectAllocator.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
class ISetAllocated : public KObjectAllocator<Derived>::AllocatedSetHookType
|
||||
{
|
||||
public:
|
||||
static void InitializeAllocator(void *buffer, size_t capacity) noexcept
|
||||
{
|
||||
allocator.GetSlabHeap().initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
void *operator new(size_t sz) noexcept
|
||||
{
|
||||
kassert(sz == sizeof(Derived));
|
||||
return allocator.GetSlabHeap().allocate();
|
||||
}
|
||||
|
||||
void operator delete(void *ptr) noexcept
|
||||
{
|
||||
allocator.GetSlabHeap().deallocate((Derived *)ptr);
|
||||
}
|
||||
|
||||
void AddToAllocatedSet() noexcept
|
||||
{
|
||||
Derived *d = (Derived *)this;
|
||||
allocator.RegisterObject(*d);
|
||||
isRegisteredToAllocator = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void RemoveFromAllocatedSet() noexcept
|
||||
{
|
||||
Derived *d = (Derived *)this;
|
||||
allocator.UnregisterObject(*d);
|
||||
}
|
||||
|
||||
virtual ~ISetAllocated()
|
||||
{
|
||||
if (isRegisteredToAllocator) {
|
||||
RemoveFromAllocatedSet();
|
||||
isRegisteredToAllocator = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool isRegisteredToAllocator = false;
|
||||
|
||||
protected:
|
||||
static KObjectAllocator<Derived> allocator;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
KObjectAllocator<Derived> ISetAllocated<Derived>::allocator{};
|
||||
|
||||
}
|
||||
35
mesosphere/include/mesosphere/interfaces/ISlabAllocated.hpp
Normal file
35
mesosphere/include/mesosphere/interfaces/ISlabAllocated.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KSlabHeap.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
class ISlabAllocated
|
||||
{
|
||||
public:
|
||||
static void InitializeSlabHeap(void *buffer, size_t capacity) noexcept
|
||||
{
|
||||
slabHeap.initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
void *operator new(size_t sz) noexcept
|
||||
{
|
||||
kassert(sz == sizeof(Derived));
|
||||
return slabHeap.allocate();
|
||||
}
|
||||
|
||||
void operator delete(void *ptr) noexcept
|
||||
{
|
||||
slabHeap.deallocate((Derived *)ptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
static KSlabHeap<Derived> slabHeap;
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
KSlabHeap<Derived> ISlabAllocated<Derived>::slabHeap{};
|
||||
|
||||
}
|
||||
29
mesosphere/include/mesosphere/interfaces/IWork.hpp
Normal file
29
mesosphere/include/mesosphere/interfaces/IWork.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <boost/intrusive/slist.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
struct WorkSListTag;
|
||||
|
||||
using WorkSListBaseHook = boost::intrusive::slist_base_hook<
|
||||
boost::intrusive::tag<WorkSListTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
|
||||
/// Bottom half in Linux jargon
|
||||
class IWork : public WorkSListBaseHook {
|
||||
public:
|
||||
virtual void DoWork() = 0;
|
||||
};
|
||||
|
||||
using WorkSList = boost::intrusive::make_slist<
|
||||
IWork,
|
||||
boost::intrusive::base_hook<WorkSListBaseHook>,
|
||||
boost::intrusive::cache_last<true>,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/interrupts/KAlarm.hpp
Normal file
34
mesosphere/include/mesosphere/interrupts/KAlarm.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IInterruptibleWork.hpp>
|
||||
#include <mesosphere/interfaces/IAlarmable.hpp>
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
#include <mesosphere/board/KSystemClock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KAlarm final : public IInterruptibleWork {
|
||||
public:
|
||||
|
||||
//KAlarm() = default;
|
||||
|
||||
/// Precondition: alarmable not already added
|
||||
void AddAlarmable(IAlarmable &alarmable);
|
||||
|
||||
/// Precondition: alarmable is present
|
||||
void RemoveAlarmable(const IAlarmable &alarmable);
|
||||
|
||||
void HandleAlarm();
|
||||
|
||||
KAlarm(const KAlarm &) = delete;
|
||||
KAlarm(KAlarm &&) = delete;
|
||||
KAlarm &operator=(const KAlarm &) = delete;
|
||||
KAlarm &operator=(KAlarm &&) = delete;
|
||||
|
||||
private:
|
||||
mutable KInterruptSpinLock<false> spinlock{};
|
||||
AlarmableSetType alarmables{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
inline void IncrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
inline void DecrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
|
||||
class KInterruptBottomHalfGuard final {
|
||||
public:
|
||||
KInterruptBottomHalfGuard()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
~KInterruptBottomHalfGuard()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
DecrementInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
KInterruptBottomHalfGuard(const KInterruptBottomHalfGuard &) = delete;
|
||||
KInterruptBottomHalfGuard(KInterruptBottomHalfGuard &&) = delete;
|
||||
KInterruptBottomHalfGuard &operator=(const KInterruptBottomHalfGuard &) = delete;
|
||||
KInterruptBottomHalfGuard &operator=(KInterruptBottomHalfGuard &&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/interrupts/KInterruptEvent.hpp
Normal file
34
mesosphere/include/mesosphere/interrupts/KInterruptEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KInterruptEvent final : public KReadableEvent, public ISetAllocated<KInterruptEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(ReadableEvent, InterruptEvent);
|
||||
|
||||
void *operator new(size_t sz) noexcept { return ISetAllocated<KInterruptEvent>::operator new(sz); }
|
||||
void operator delete(void *ptr) noexcept { ISetAllocated<KInterruptEvent>::operator delete(ptr); }
|
||||
|
||||
virtual ~KInterruptEvent();
|
||||
Result Initialize(int irqId, u32 flags);
|
||||
|
||||
private:
|
||||
// TODO: receiver
|
||||
int irqId = -1;
|
||||
};
|
||||
|
||||
inline void intrusive_ptr_add_ref(KInterruptEvent *obj)
|
||||
{
|
||||
intrusive_ptr_add_ref((KAutoObject *)obj);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(KInterruptEvent *obj)
|
||||
{
|
||||
intrusive_ptr_release((KAutoObject *)obj);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KCoreContext.hpp>
|
||||
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
|
||||
#include <mesosphere/arch/KSpinLock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
inline void IncrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
inline void DecrementThreadInterruptBottomHalfLockCount(KThread &thread);
|
||||
|
||||
template<bool disableInterrupts = false>
|
||||
class KInterruptSpinLock : public KSpinLock {
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
if (!KSpinLock::try_lock()) {
|
||||
DecrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
IncrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
KSpinLock::lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
KThread *curThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
|
||||
KSpinLock::unlock();
|
||||
DecrementThreadInterruptBottomHalfLockCount(*curThread);
|
||||
}
|
||||
|
||||
KInterruptSpinLock() = default;
|
||||
KInterruptSpinLock(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock(KInterruptSpinLock &&) = delete;
|
||||
KInterruptSpinLock &operator=(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock &operator=(KInterruptSpinLock &&) = delete;
|
||||
};
|
||||
|
||||
template<>
|
||||
class KInterruptSpinLock<true> : public KSpinLock {
|
||||
public:
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
if (!KSpinLock::try_lock()) {
|
||||
RestoreInterrupts(flags);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
flags = MaskInterrupts();
|
||||
KSpinLock::lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
KSpinLock::unlock();
|
||||
RestoreInterrupts(flags);
|
||||
}
|
||||
|
||||
KInterruptSpinLock() = default;
|
||||
KInterruptSpinLock(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock(KInterruptSpinLock &&) = delete;
|
||||
KInterruptSpinLock &operator=(const KInterruptSpinLock &) = delete;
|
||||
KInterruptSpinLock &operator=(KInterruptSpinLock &&) = delete;
|
||||
|
||||
private:
|
||||
InterruptFlagsType flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
27
mesosphere/include/mesosphere/interrupts/KWorkQueue.hpp
Normal file
27
mesosphere/include/mesosphere/interrupts/KWorkQueue.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interfaces/IWork.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KWorkQueue final {
|
||||
public:
|
||||
|
||||
void AddWork(IWork &work);
|
||||
void Initialize();
|
||||
|
||||
void HandleWorkQueue();
|
||||
|
||||
KWorkQueue(const KWorkQueue &) = delete;
|
||||
KWorkQueue(KWorkQueue &&) = delete;
|
||||
KWorkQueue &operator=(const KWorkQueue &) = delete;
|
||||
KWorkQueue &operator=(KWorkQueue &&) = delete;
|
||||
|
||||
private:
|
||||
WorkSList workQueue{};
|
||||
SharedPtr<KThread> handlerThread{};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/kresources/KSlabHeap.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KObjectAllocator {
|
||||
private:
|
||||
struct Comparator {
|
||||
constexpr bool operator()(const T &lhs, const T &rhs) const
|
||||
{
|
||||
return lhs.GetComparisonKey() < rhs.GetComparisonKey();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ComparatorEqual {
|
||||
constexpr u64 operator()(const T &val) const
|
||||
{
|
||||
return val.GetComparisonKey();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
struct HookTag;
|
||||
|
||||
using AllocatedSetHookType = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::tag<HookTag>,
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>
|
||||
>;
|
||||
using AllocatedSetType = typename
|
||||
boost::intrusive::make_set<
|
||||
T,
|
||||
boost::intrusive::base_hook<AllocatedSetHookType>,
|
||||
boost::intrusive::compare<Comparator>
|
||||
>::type;
|
||||
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
AllocatedSetType &GetAllocatedSet()
|
||||
{
|
||||
return allocatedSet;
|
||||
}
|
||||
|
||||
KSlabHeap<T> &GetSlabHeap()
|
||||
{
|
||||
return slabHeap;
|
||||
}
|
||||
|
||||
void RegisterObject(T &obj) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
allocatedSet.insert(obj);
|
||||
}
|
||||
|
||||
void UnregisterObject(T &obj) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
allocatedSet.erase(obj);
|
||||
}
|
||||
|
||||
SharedPtr<T> FindObject(u64 comparisonKey) noexcept
|
||||
{
|
||||
std::scoped_lock guard{mutex};
|
||||
auto it = allocatedSet.find(comparisonKey, ComparatorEqual{});
|
||||
return it != allocatedSet.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
AllocatedSetType allocatedSet{};
|
||||
KSlabHeap<T> slabHeap{};
|
||||
mutable KMutex mutex{};
|
||||
};
|
||||
|
||||
}
|
||||
74
mesosphere/include/mesosphere/kresources/KResourceLimit.hpp
Normal file
74
mesosphere/include/mesosphere/kresources/KResourceLimit.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/threading/KConditionVariable.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KTransferMemory;
|
||||
|
||||
class KResourceLimit final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KResourceLimit>
|
||||
{
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, ResourceLimit);
|
||||
|
||||
enum class Category : uint {
|
||||
Memory = 0,
|
||||
Threads,
|
||||
Events,
|
||||
TransferMemories,
|
||||
Sessions,
|
||||
|
||||
Max,
|
||||
};
|
||||
|
||||
static constexpr Category GetCategory(KAutoObject::TypeId typeId) {
|
||||
switch (typeId) {
|
||||
case KAutoObject::TypeId::Thread: return Category::Threads;
|
||||
case KAutoObject::TypeId::Event: return Category::Events;
|
||||
case KAutoObject::TypeId::TransferMemory: return Category::TransferMemories;
|
||||
case KAutoObject::TypeId::Session: return Category::Sessions;
|
||||
case KAutoObject::TypeId::LightSession: return Category::Sessions;
|
||||
default: return Category::Max;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> static constexpr Category categoryOf = GetCategory(T::typeId);
|
||||
|
||||
static KResourceLimit &GetDefaultInstance() { return defaultInstance; }
|
||||
|
||||
size_t GetCurrentValue(Category category) const;
|
||||
size_t GetLimitValue(Category category) const;
|
||||
size_t GetRemainingValue(Category category) const;
|
||||
|
||||
bool SetLimitValue(Category category, size_t value);
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
bool Reserve(Category category, size_t count, const std::chrono::duration<Rep, Period>& timeout)
|
||||
{
|
||||
return ReserveDetail(category, count, KSystemClock::now() + timeout);
|
||||
}
|
||||
|
||||
void Release(Category category, size_t count, size_t realCount);
|
||||
|
||||
private:
|
||||
|
||||
static KResourceLimit defaultInstance;
|
||||
bool ReserveDetail(Category category, size_t count, const KSystemClock::time_point &timeoutTime);
|
||||
|
||||
// Signed in official kernel
|
||||
size_t limitValues[(size_t)Category::Max] = {};
|
||||
|
||||
// Current value: real value + dangling resources about to be released
|
||||
size_t currentValues[(size_t)Category::Max] = {};
|
||||
size_t realValues[(size_t)Category::Max] = {};
|
||||
|
||||
mutable KConditionVariable condvar{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ResourceLimit);
|
||||
|
||||
}
|
||||
55
mesosphere/include/mesosphere/kresources/KSlabHeap.hpp
Normal file
55
mesosphere/include/mesosphere/kresources/KSlabHeap.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/kresources/KSlabStack.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KSlabHeap {
|
||||
public:
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
private:
|
||||
KSlabStack<T> stack{};
|
||||
size_t capacity = 0;
|
||||
T *bufferStart = nullptr;
|
||||
|
||||
public:
|
||||
T *allocate() noexcept
|
||||
{
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
void deallocate(T *elem) noexcept
|
||||
{
|
||||
kassert(elem >= bufferStart && elem < bufferStart + capacity);
|
||||
stack.push(elem);
|
||||
}
|
||||
|
||||
constexpr size_t size() const
|
||||
{
|
||||
return capacity;
|
||||
}
|
||||
|
||||
KSlabHeap() noexcept = default;
|
||||
|
||||
void initialize(void *buffer, size_t capacity)
|
||||
{
|
||||
this->capacity = capacity;
|
||||
this->bufferStart = (T *)buffer;
|
||||
stack.initialize(buffer, capacity);
|
||||
}
|
||||
|
||||
KSlabHeap(void *buffer, size_t capacity) noexcept : stack(buffer, capacity), capacity(capacity), bufferStart((T *)buffer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
78
mesosphere/include/mesosphere/kresources/KSlabStack.hpp
Normal file
78
mesosphere/include/mesosphere/kresources/KSlabStack.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <atomic>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class KSlabStack {
|
||||
public:
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using void_pointer = void *;
|
||||
using const_void_ptr = const void *;
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
Node *next;
|
||||
};
|
||||
|
||||
std::atomic<Node *> head;
|
||||
public:
|
||||
|
||||
void push(T *data) noexcept
|
||||
{
|
||||
Node *newHead = (Node *)data;
|
||||
Node *oldHead = head.load();
|
||||
do {
|
||||
newHead->next = oldHead;
|
||||
} while(!head.compare_exchange_weak(oldHead, newHead));
|
||||
}
|
||||
|
||||
T *pop() noexcept
|
||||
{
|
||||
Node *newHead;
|
||||
Node *oldHead = head.load();
|
||||
if (oldHead == nullptr) {
|
||||
return nullptr;
|
||||
} else {
|
||||
do {
|
||||
newHead = oldHead == nullptr ? oldHead : oldHead->next;
|
||||
} while(!head.compare_exchange_weak(oldHead, newHead));
|
||||
|
||||
return (T *)oldHead;
|
||||
}
|
||||
}
|
||||
|
||||
KSlabStack() noexcept = default;
|
||||
|
||||
// Not reentrant (unlike NN's init function)
|
||||
void initialize(void *buffer, size_t size) noexcept
|
||||
{
|
||||
T *ar = (T *)buffer;
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node *ndlast = (Node *)&ar[size - 1];
|
||||
ndlast->next = nullptr;
|
||||
|
||||
for (size_t i = 0; i < size - 1; i++) {
|
||||
Node *nd = (Node *)&ar[i];
|
||||
Node *ndnext = (Node *)&ar[i + 1];
|
||||
nd->next = ndnext;
|
||||
}
|
||||
|
||||
Node *ndfirst = (Node *)&ar[0];
|
||||
head.store(ndfirst);
|
||||
}
|
||||
|
||||
KSlabStack(void *buffer, size_t size) { initialize(buffer, size); }
|
||||
};
|
||||
|
||||
}
|
||||
38
mesosphere/include/mesosphere/processes/KClientPort.hpp
Normal file
38
mesosphere/include/mesosphere/processes/KClientPort.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
class KServerPort;
|
||||
|
||||
class KClientPort final :
|
||||
public KSynchronizationObject,
|
||||
public IClient<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ClientPort);
|
||||
|
||||
virtual ~KClientPort();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
std::tuple<Result, SharedPtr<KLightClientSession>> ConnectLight();
|
||||
|
||||
private:
|
||||
friend class KPort;
|
||||
|
||||
std::atomic<int> numSessions{0};
|
||||
std::atomic<int> peakNumNormalSessions{0};
|
||||
int maxSessions = 0;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ClientPort);
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/processes/KEvent.hpp
Normal file
34
mesosphere/include/mesosphere/processes/KEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/processes/KReadableEvent.hpp>
|
||||
#include <mesosphere/processes/KWritableEvent.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KEvent final :
|
||||
public KAutoObject,
|
||||
public IClientServerParent<KEvent, KReadableEvent, KWritableEvent>,
|
||||
public ISetAllocated<KEvent>,
|
||||
public ILimitedResource<KEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Event);
|
||||
MESOSPHERE_CLIENT_SERVER_PARENT_TRAITS(ReadableEvent, WritableEvent);
|
||||
MESOSPHERE_LIMITED_RESOURCE_TRAITS(10s);
|
||||
|
||||
virtual ~KEvent();
|
||||
|
||||
Result Initialize();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Event);
|
||||
|
||||
}
|
||||
78
mesosphere/include/mesosphere/processes/KHandleTable.hpp
Normal file
78
mesosphere/include/mesosphere/processes/KHandleTable.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Handle.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KThread;
|
||||
class KProcess;
|
||||
|
||||
class KHandleTable final {
|
||||
public:
|
||||
|
||||
static constexpr size_t capacityLimit = 1024;
|
||||
static constexpr Handle selfThreadAlias{0, -1, true};
|
||||
static constexpr Handle selfProcessAlias{1, -1, true};
|
||||
|
||||
template<typename T>
|
||||
SharedPtr<T> Get(Handle handle, bool allowAlias = true) const
|
||||
{
|
||||
if constexpr (std::is_same_v<T, KAutoObject>) {
|
||||
(void)allowAlias;
|
||||
return GetAutoObject(handle);
|
||||
} else if constexpr (std::is_same_v<T, KThread>) {
|
||||
return GetThread(handle, allowAlias);
|
||||
} else if constexpr (std::is_same_v<T, KProcess>) {
|
||||
return GetProcess(handle, allowAlias);
|
||||
} else {
|
||||
return DynamicObjectCast<T>(GetAutoObject(handle));
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<Result, Handle> Generate(SharedPtr<KAutoObject> obj);
|
||||
|
||||
/// For deferred-init
|
||||
Result Set(SharedPtr<KAutoObject> obj, Handle handle);
|
||||
|
||||
bool Close(Handle handle);
|
||||
void Destroy();
|
||||
|
||||
constexpr size_t GetNumActive() const { return numActive; }
|
||||
constexpr size_t GetSize() const { return size; }
|
||||
constexpr size_t GetCapacity() const { return capacity; }
|
||||
|
||||
Result Initialize(size_t capacity); // TODO: implement!
|
||||
|
||||
~KHandleTable();
|
||||
|
||||
private:
|
||||
|
||||
bool IsValid(Handle handle) const;
|
||||
SharedPtr<KAutoObject> GetAutoObject(Handle handle) const;
|
||||
SharedPtr<KThread> GetThread(Handle handle, bool allowAlias = true) const;
|
||||
SharedPtr<KProcess> GetProcess(Handle handle, bool allowAlias = true) const;
|
||||
|
||||
struct Entry {
|
||||
SharedPtr<KAutoObject> object{};
|
||||
s16 id = 0;
|
||||
};
|
||||
|
||||
std::array<Entry, capacityLimit> entries{};
|
||||
|
||||
// Here the official kernel uses pointer, Yuzu and ourselves are repurposing a field in Entry instead.
|
||||
s16 firstFreeIndex = 0;
|
||||
s16 idCounter = 1;
|
||||
|
||||
u16 numActive = 0, size = 0, capacity = 0;
|
||||
|
||||
mutable KInterruptSpinLock<false> spinlock;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KLightSession;
|
||||
class KClientPort;
|
||||
|
||||
struct LightSessionRequest;
|
||||
|
||||
class KLightClientSession final : public KAutoObject, public IClient<KLightSession, KLightClientSession, KLightServerSession> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightClientSession);
|
||||
|
||||
virtual ~KLightClientSession();
|
||||
|
||||
Result SendSyncRequest(LightSessionRequest *request);
|
||||
|
||||
private:
|
||||
friend class KLightSession;
|
||||
|
||||
SharedPtr<KClientPort> parentClientPort = nullptr;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightClientSession);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/IServer.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KLightClientSession;
|
||||
class KLightSession;
|
||||
class KClientPort;
|
||||
|
||||
struct LightSessionRequest;
|
||||
|
||||
struct LightServerSessionListTag;
|
||||
using LightServerSessionListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<LightServerSessionListTag> >;
|
||||
|
||||
class KLightServerSession final :
|
||||
public KAutoObject,
|
||||
public IServer<KLightSession, KLightClientSession, KLightServerSession>,
|
||||
public LightServerSessionListBaseHook {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightServerSession);
|
||||
|
||||
using List = typename boost::intrusive::make_list<
|
||||
KLightServerSession,
|
||||
boost::intrusive::base_hook<LightServerSessionListBaseHook>,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
virtual ~KLightServerSession();
|
||||
|
||||
/// Needs to be called from critical section
|
||||
Result HandleSyncRequest(KThread &sender);
|
||||
|
||||
Result ReplyAndReceive(LightSessionRequest *request);
|
||||
private:
|
||||
friend class KLightSession;
|
||||
|
||||
void Terminate(bool fromServer);
|
||||
|
||||
KThread::WaitList senderThreads{}, receiverThreads{};
|
||||
SharedPtr<KThread> currentSender{};
|
||||
KThread *currentReceiver = nullptr;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightServerSession);
|
||||
|
||||
}
|
||||
46
mesosphere/include/mesosphere/processes/KLightSession.hpp
Normal file
46
mesosphere/include/mesosphere/processes/KLightSession.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/ILimitedResource.hpp>
|
||||
#include <mesosphere/processes/KLightClientSession.hpp>
|
||||
#include <mesosphere/processes/KLightServerSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
|
||||
struct LightSessionRequest {
|
||||
s32 cmdId;
|
||||
u32 data[6];
|
||||
};
|
||||
|
||||
class KLightSession final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KLightSession>,
|
||||
public ILimitedResource<KLightSession>,
|
||||
public IClientServerParent<KLightSession, KLightClientSession, KLightServerSession> {
|
||||
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, LightSession);
|
||||
MESOSPHERE_LIMITED_RESOURCE_TRAITS(10s);
|
||||
|
||||
virtual ~KLightSession();
|
||||
|
||||
Result Initialize(KPort *parentPort = nullptr);
|
||||
|
||||
private:
|
||||
friend class KLightClientSession;
|
||||
friend class KLightServerSession;
|
||||
|
||||
void Terminate(bool fromServer);
|
||||
bool isClientAlive = false;
|
||||
bool isServerAlive = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(LightSession);
|
||||
|
||||
}
|
||||
39
mesosphere/include/mesosphere/processes/KPort.hpp
Normal file
39
mesosphere/include/mesosphere/processes/KPort.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/interfaces/IClientServerParent.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/processes/KClientPort.hpp>
|
||||
#include <mesosphere/processes/KServerPort.hpp>
|
||||
#include <mesosphere/processes/KLightSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort final :
|
||||
public KAutoObject,
|
||||
public ISetAllocated<KPort>,
|
||||
public IClientServerParent<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Port);
|
||||
|
||||
virtual ~KPort();
|
||||
|
||||
Result Initialize(int maxSessions, bool isLight);
|
||||
|
||||
Result AddLightServerSession(KLightServerSession &lightServerSession);
|
||||
|
||||
private:
|
||||
friend class KClientPort;
|
||||
friend class KServerPort;
|
||||
|
||||
bool isClientAlive = false;
|
||||
bool isServerAlive = false;
|
||||
bool isLight = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Port);
|
||||
|
||||
}
|
||||
79
mesosphere/include/mesosphere/processes/KProcess.hpp
Normal file
79
mesosphere/include/mesosphere/processes/KProcess.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
class KThread;
|
||||
class KResourceLimit;
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/processes/KHandleTable.hpp>
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KProcess final : public KSynchronizationObject /* FIXME */ {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, Process);
|
||||
|
||||
enum class State : uint {
|
||||
Created = 0,
|
||||
CreatedAttached,
|
||||
Started,
|
||||
Crashed,
|
||||
StartedAttached,
|
||||
Exiting,
|
||||
Exited,
|
||||
DebugSuspended,
|
||||
};
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
constexpr long GetSchedulerOperationCount() const { return schedulerOperationCount; }
|
||||
|
||||
void IncrementSchedulerOperationCount() { ++schedulerOperationCount; }
|
||||
void SetLastThreadAndIdleSelectionCount(KThread *thread, ulong idleSelectionCount);
|
||||
|
||||
const SharedPtr<KResourceLimit> &GetResourceLimit() const { return reslimit; }
|
||||
|
||||
KHandleTable &GetHandleTable() { return handleTable; }
|
||||
|
||||
constexpr State GetState() const { return state; }
|
||||
|
||||
void SetDebugPauseState(bool pause);
|
||||
|
||||
KDebug *GetDebug() const { return debug; }
|
||||
void SetDebug(KDebug *debug);
|
||||
void ClearDebug(State attachState);
|
||||
|
||||
template<typename F, typename ...Args>
|
||||
void ForEachThread(F f, Args &&...args)
|
||||
{
|
||||
std::scoped_lock s{mutex};
|
||||
std::scoped_lock s2{threadingMutex};
|
||||
|
||||
for (KThread &t : threadList) {
|
||||
f(t, std::forward(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KThread::ProcessList threadList{};
|
||||
|
||||
KThread *lastThreads[MAX_CORES]{nullptr};
|
||||
ulong lastIdleSelectionCount[MAX_CORES]{0};
|
||||
long schedulerOperationCount = -1;
|
||||
|
||||
State state = State::Created;
|
||||
bool stateChanged = false;
|
||||
|
||||
KDebug *debug = nullptr;
|
||||
SharedPtr<KResourceLimit> reslimit{};
|
||||
KHandleTable handleTable{};
|
||||
|
||||
mutable KMutex mutex{}, threadingMutex{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Process);
|
||||
|
||||
}
|
||||
34
mesosphere/include/mesosphere/processes/KReadableEvent.hpp
Normal file
34
mesosphere/include/mesosphere/processes/KReadableEvent.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/interfaces/IClient.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KWritableEvent;
|
||||
class KEvent;
|
||||
|
||||
// Inherited by KInterruptEvent
|
||||
class KReadableEvent : public KSynchronizationObject, public IClient<KEvent, KReadableEvent, KWritableEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ReadableEvent);
|
||||
MESOSPHERE_CLIENT_TRAITS(Event);
|
||||
|
||||
virtual ~KReadableEvent();
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
Result Reset();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
private:
|
||||
bool isSignaled = false;
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ReadableEvent);
|
||||
|
||||
}
|
||||
33
mesosphere/include/mesosphere/processes/KServerPort.hpp
Normal file
33
mesosphere/include/mesosphere/processes/KServerPort.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/KSynchronizationObject.hpp>
|
||||
#include <mesosphere/processes/KLightServerSession.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KPort;
|
||||
class KClientPort;
|
||||
|
||||
class KServerPort final :
|
||||
public KSynchronizationObject,
|
||||
public IServer<KPort, KClientPort, KServerPort> {
|
||||
|
||||
public:
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(SynchronizationObject, ServerPort);
|
||||
|
||||
virtual ~KServerPort();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
private:
|
||||
friend class KPort;
|
||||
Result AddLightServerSession(KLightServerSession &lightServerSession);
|
||||
|
||||
KLightServerSession::List lightServerSessions{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(ServerPort);
|
||||
|
||||
}
|
||||
33
mesosphere/include/mesosphere/processes/KWritableEvent.hpp
Normal file
33
mesosphere/include/mesosphere/processes/KWritableEvent.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/core/util.hpp>
|
||||
#include <mesosphere/core/Result.hpp>
|
||||
#include <mesosphere/core/KAutoObject.hpp>
|
||||
#include <mesosphere/interfaces/ISetAllocated.hpp>
|
||||
#include <mesosphere/interfaces/IServer.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KReadableEvent;
|
||||
class KEvent;
|
||||
|
||||
class KWritableEvent final : public KAutoObject, public IServer<KEvent, KReadableEvent, KWritableEvent> {
|
||||
public:
|
||||
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, WritableEvent);
|
||||
MESOSPHERE_SERVER_TRAITS(Event);
|
||||
|
||||
virtual ~KWritableEvent();
|
||||
|
||||
Result Signal();
|
||||
Result Clear();
|
||||
|
||||
private:
|
||||
friend class KEvent;
|
||||
|
||||
SharedPtr<KReadableEvent> client{};
|
||||
};
|
||||
|
||||
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(WritableEvent);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/threading/KThread.hpp>
|
||||
#include <mesosphere/threading/KMutex.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
|
||||
/// Provides an interface similar to std::condition_variable
|
||||
class KConditionVariable final {
|
||||
public:
|
||||
|
||||
using native_handle_type = uiptr;
|
||||
|
||||
KConditionVariable() = default;
|
||||
KConditionVariable(const KConditionVariable &) = delete;
|
||||
KConditionVariable(KConditionVariable &&) = delete;
|
||||
KConditionVariable &operator=(const KConditionVariable &) = delete;
|
||||
KConditionVariable &operator=(KConditionVariable &&) = delete;
|
||||
|
||||
native_handle_type native_handle() { return mutex_.native_handle(); }
|
||||
|
||||
KMutex &mutex() { return mutex_; }
|
||||
|
||||
void wait() noexcept
|
||||
{
|
||||
wait_until_impl(KSystemClock::never);
|
||||
}
|
||||
template<typename Predicate>
|
||||
void wait(Predicate pred)
|
||||
{
|
||||
while (!pred()) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Clock, typename Duration>
|
||||
void wait_until(const std::chrono::time_point<Clock, Duration> &timeoutPoint) noexcept
|
||||
{
|
||||
wait_until_impl(timeoutPoint);
|
||||
}
|
||||
template<typename Clock, typename Duration, typename Predicate>
|
||||
bool wait_until(const std::chrono::time_point<Clock, Duration> &timeoutPoint, Predicate pred)
|
||||
{
|
||||
while (!pred()) {
|
||||
wait_until(timeoutPoint);
|
||||
if (Clock::now() >= timeoutPoint) {
|
||||
return pred();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
void wait_for(const std::chrono::duration<Rep, Period>& timeout) noexcept
|
||||
{
|
||||
wait_until(KSystemClock::now() + timeout);
|
||||
}
|
||||
|
||||
template<typename Rep, typename Period, typename Predicate>
|
||||
bool wait_for(const std::chrono::duration<Rep, Period>& timeout, Predicate pred)
|
||||
{
|
||||
return wait_until(KSystemClock::now() + timeout, std::move(pred));
|
||||
}
|
||||
|
||||
void notify_one() noexcept;
|
||||
void notify_all() noexcept;
|
||||
|
||||
private:
|
||||
void wait_until_impl(const KSystemClock::time_point &timeoutPoint) noexcept;
|
||||
|
||||
KMutex mutex_{};
|
||||
KThread::WaitList waiterList{};
|
||||
};
|
||||
|
||||
}
|
||||
26
mesosphere/include/mesosphere/threading/KCriticalSection.hpp
Normal file
26
mesosphere/include/mesosphere/threading/KCriticalSection.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <mesosphere/interrupts/KInterruptSpinLock.hpp>
|
||||
|
||||
namespace mesosphere
|
||||
{
|
||||
|
||||
class KCriticalSection final : public KInterruptSpinLock<false> {
|
||||
public:
|
||||
|
||||
bool try_lock();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
KCriticalSection() = default;
|
||||
KCriticalSection(const KCriticalSection &) = delete;
|
||||
KCriticalSection(KCriticalSection &&) = delete;
|
||||
KCriticalSection &operator=(const KCriticalSection &) = delete;
|
||||
KCriticalSection &operator=(KCriticalSection &&) = delete;
|
||||
|
||||
private:
|
||||
KThread *lockingThread = nullptr;
|
||||
ulong lockCount = 0;
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user