Compare commits

...

137 Commits

Author SHA1 Message Date
TuxSH
2efdee5cb8 meso: KProcess: add ForEachThread 2019-06-03 17:41:09 -07:00
TuxSH
240f455bc0 Fix FindObject 2019-06-03 17:41:09 -07:00
TuxSH
6642373795 meso: add FindObject to KObjectAllocator * 2019-06-03 17:41:09 -07:00
TuxSH
1a924ad317 meso: invert process<>thread header deps 2019-06-03 17:41:09 -07:00
TuxSH
91662decf0 meso: add KProcess::SetDebugPauseState 2019-06-03 17:41:09 -07:00
TuxSH
529024448e meso: introduce ProcessState 2019-06-03 17:41:09 -07:00
TuxSH
757aa30e74 meso: Implement KObjectRegistry 2019-06-03 17:41:09 -07:00
TuxSH
fb4e0988b9 meso: KServerPort dtor 2019-06-03 17:41:09 -07:00
TuxSH
ad879ca327 meso: Implement ConnectLight 2019-06-03 17:41:08 -07:00
TuxSH
efe7325af3 meso: impl AddServerSession 2019-06-03 17:41:08 -07:00
TuxSH
173bde2eca meso: KPort mini skeleton 2019-06-03 17:41:08 -07:00
TuxSH
acf32f841c meso: Implement LightSession functions 2019-06-03 17:41:08 -07:00
TuxSH
be3550d382 meso: Implement KLightServerSession dtor 2019-06-03 17:41:08 -07:00
TuxSH
9c8f818c29 meso: add KLightSession skeleton, remove KBaseSession 2019-06-03 17:41:08 -07:00
TuxSH
eb7e4153d1 meso: use macro to refactor auto obj declaration 2019-06-03 17:41:08 -07:00
TuxSH
86c43331eb meso: KBaseSession 2019-06-03 17:41:08 -07:00
TuxSH
baa34ddab5 meso: same thing for KEvent 2019-06-03 17:41:07 -07:00
TuxSH
d9c97983a3 meso: Client/server ifaces: use friendship 2019-06-03 17:41:07 -07:00
TuxSH
b6bbc4f3e5 arm64.hpp => arch.hpp, add GetCurrentCoreContextInstance 2019-06-03 17:41:07 -07:00
TuxSH
195da2e599 Remove IServer client ref 2019-06-03 17:41:07 -07:00
TuxSH
1b3b26c3af Fix KEvent resource acquision timeout 2019-06-03 17:41:06 -07:00
TuxSH
0fb40d1ef5 Implement KCriticalSection (mostly) 2019-06-03 17:41:06 -07:00
TuxSH
4238d2e97f Add KInterruptSpinLock, which is what is really used in the official kernel 2019-06-03 17:41:06 -07:00
TuxSH
7b726c3184 Add KInterruptBottomHalfGuard 2019-06-03 17:41:05 -07:00
TuxSH
40b860c239 Removing useless func IsAlive, since it's handled by ISetAllocated 2019-06-03 17:41:05 -07:00
TuxSH
5222b429c5 Add KInterruptEvent skeleton; remove useless funcs 2019-06-03 17:41:05 -07:00
TuxSH
2949d08eb2 Add static assers to token generation 2019-06-03 17:41:05 -07:00
TuxSH
6d898acc98 Fix make_object 2019-06-03 17:41:04 -07:00
TuxSH
f72836d72c Fix GenerateClassToken 2019-06-03 17:41:04 -07:00
TuxSH
65b20d0685 Better solution for latest commit 2019-06-03 17:41:04 -07:00
TuxSH
d4241fd8ef Add operator new/delete in IClient/IServer 2019-06-03 17:41:04 -07:00
TuxSH
504c74bc57 Fix build issues in make_object; fix linkage issues 2019-06-03 17:41:04 -07:00
TuxSH
c47a9931d9 Handle table fixes & additions to makeobject 2019-06-03 17:41:04 -07:00
TuxSH
4a1021f220 Do the same for ILimitedResource; fix handle table def, for the moment 2019-06-03 17:41:04 -07:00
TuxSH
ac6762bb6c Add more reflexivity to client/server interfaces 2019-06-03 17:41:04 -07:00
TuxSH
e2d8316401 Fix KCondition variable; add timeout parameter to ILimitableResource 2019-06-03 17:41:03 -07:00
Michael Scire
0a0c05481e mesosphere: Result{} => Result() 2019-06-03 17:41:03 -07:00
TuxSH
cd1f74154d Use scoped_lock, etc 2019-06-03 17:41:03 -07:00
TuxSH
698fa9fcb0 Write KEvent::Initialize, fix build issues 2019-06-03 17:41:03 -07:00
Michael Scire
a4419dfc41 mesosphere: Skeleton K(Readable/Writable)Event 2019-06-03 17:41:03 -07:00
Michael Scire
600afa5f0f mesosphere: Add convenience KScopedCriticalSection 2019-06-03 17:41:03 -07:00
Michael Scire
9b1fb0c6df mesosphere: KSync->Signal() -> KSync->NotifyWaiters 2019-06-03 17:41:02 -07:00
Michael Scire
08970a82ea mesosphere: ResultNotPermitted -> ResultInvalidState 2019-06-03 17:41:02 -07:00
TuxSH
a035be66bd Rename initialize for client/server/parent 2019-06-03 17:41:02 -07:00
TuxSH
9318ab10b2 Add Client/Server interfaces 2019-06-03 17:41:02 -07:00
TuxSH
dffb233423 Add KThread::StackParameters 2019-06-03 17:41:02 -07:00
TuxSH
e57203a7d4 Implement WaitSynchronization 2019-06-03 17:41:02 -07:00
TuxSH
7fde5fbe40 Implement KSynchronizationObject 2019-06-03 17:41:02 -07:00
TuxSH
1684e1d35c Move KAutoObject.hpp to core/ 2019-06-03 17:41:01 -07:00
Michael Scire
edcd4cbc26 Result: Refactor to have compiletime defs 2019-06-03 17:41:01 -07:00
TuxSH
be17f1f494 Make Result produce the same code as Nintendo's 2019-06-03 17:41:01 -07:00
TuxSH
672204c993 Put handle in its own file & fix it 2019-06-03 17:41:01 -07:00
TuxSH
6166262b5c Fix build issues 2019-06-03 17:41:00 -07:00
TuxSH
b492096aed Add Result.hpp 2019-06-03 17:41:00 -07:00
TuxSH
cfeebbd1c9 Fix KLinkedList; make it work even with strict aliasing 2019-06-03 17:41:00 -07:00
TuxSH
4078c9a07d Implement KLinkedList; untested 2019-06-03 17:41:00 -07:00
TuxSH
ed982877bd Remove bugous MLQ reverse iterator 2019-06-03 17:41:00 -07:00
TuxSH
745fa84e5e Add mesosphere (VERY VERY WIP) 2019-06-03 17:41:00 -07:00
Michael Scire
50e307b4b7 Update for latest libnx 2019-06-03 12:19:05 -07:00
SciresM
4387da1ecc Add licensing exemption for yuzu. 2019-05-30 18:20:24 -07:00
Michael Scire
d6502c174a pm: actually implement GetBootFinishedEvent 2019-05-27 21:52:28 -07:00
Michael Scire
f38965d0bd dmnt: implement debug log opcode 2019-05-27 18:44:09 -07:00
SciresM
c2cb94062a Merge pull request #548 from Thog/feature/creport-32-stack-frames
creport: Add 32 bits stack frames parsing support
2019-05-27 18:11:10 -07:00
Michael Scire
cfa050cc8f dmnt: explicitly reserve double-extended width opcodes 2019-05-27 18:10:49 -07:00
Thomas Guillemard
bbcb3757bd Address comment 2019-05-28 00:26:56 +02:00
Thomas Guillemard
a33ace8996 creport: Add 32 bits stack frames parsing support
Also fix FP, SP and LR registers being set wrongly by svcGetDebugThreadParam for 32 bits processes.
2019-05-28 00:18:10 +02:00
Michael Scire
766097d0b7 creport: dump tls/name on crash (closes #310) 2019-05-25 13:33:33 -07:00
SciresM
5f5a8567ce docs: remove nonsense from thermo (closes #296) 2019-05-23 15:42:43 -07:00
Michael Scire
7dbe61c94c docs: mention we don't do smhax (closes #303) 2019-05-23 12:25:03 -07:00
Michael Scire
422dce6974 ams: bump version to 0.8.10 2019-05-22 12:30:37 -07:00
Michael Scire
1d429261e9 Add 0.8.10 changelog ahead of release. 2019-05-22 12:30:01 -07:00
Michael Scire
6f25e92892 set.mitm: language emulation (closes #489) 2019-05-22 12:13:10 -07:00
SciresM
6cc29185d2 Merge pull request #545 from Kronos2308/simple-sd-save-flag
simple sd_save flag
2019-05-17 07:34:07 -07:00
SciresM
13a1566b4e Merge pull request #542 from leo60228/master
Allow pressing buttons on all controllers in loader (closes #541)
2019-05-17 07:33:55 -07:00
Kronos2308
bbb658a7e5 orthography 2019-05-17 10:03:53 +01:00
Kronos2308
ad812c8125 use configuration of system_settings.ini
prioritize the configuration of system_settings.ini
2019-05-17 09:28:46 +01:00
Kronos2308
a64fdce505 flag is now "redirect_save" 2019-05-17 08:44:41 +01:00
Kronos2308
7e950c3bcc Revert "Add files via upload"
This reverts commit d0d7772f98.
2019-05-17 08:38:21 +01:00
Kronos2308
d0d7772f98 Add files via upload 2019-05-17 08:29:38 +01:00
Kronos2308
674175e1c6 simple sd_save flag
something that allows you to decide which title to redirect games saves and which does not
2019-05-17 03:28:24 +01:00
leo60228
eab2d05680 Do same in dmnt and reboot_to_payload 2019-05-12 10:43:49 -04:00
leo60228
56d7473451 Do same in ams_mitm 2019-05-12 09:04:58 -04:00
Michael Scire
bfd4d41834 boot: fix a few issues in gpio/pinmux config 2019-05-12 03:49:36 -07:00
leo60228
defee07625 Allow pressing buttons on all controllers in loader (closes #541) 2019-05-11 15:15:17 -04:00
hexkyz
2c3111f9c9 fusee: Add more verbose error messages
boot: Fix license text
2019-05-10 17:15:25 +01:00
Michael Scire
ca2c171482 fusee: how did this ever work 2019-05-10 08:40:21 -07:00
Michael Scire
cfcb1cd3a1 update libstratosphere 2019-05-10 05:36:55 -07:00
Michael Scire
bb6cc6532b fusee/exo: add ability to disable user exception handlers
please do not use this
yellows8 needs it to debug am, 99% of use cases want them on
2019-05-10 03:50:25 -07:00
Michael Scire
03a425a579 stratosphere: TODO: panic -> std::abort() 2019-05-10 03:28:18 -07:00
Michael Scire
41f5b39f6b stratosphere: stop using kernelAbove 2019-05-10 03:25:07 -07:00
Michael Scire
b5dd621250 pm: fix memory usage on 5.0 2019-05-10 03:24:50 -07:00
SciresM
27cde7da7a Merge pull request #536 from Atmosphere-NX/boot_dev
Boot: reimplement the boot sysmodule
2019-05-10 02:37:03 -07:00
Michael Scire
377b8dae9f boot: fix two logic inversions in wake pin init 2019-05-09 21:58:13 -07:00
Michael Scire
78828427a4 boot: fix writes to WAKE2_MASK 2019-05-09 20:47:18 -07:00
SciresM
fe28dac9d3 Merge pull request #537 from friedkeenan/boot_dev
boot: fix the width of the text splash
2019-05-09 12:23:34 -07:00
friedkeenan
a85102c97c boot: fix the width of the text splash 2019-05-09 14:18:57 -05:00
Michael Scire
9b11f1cb19 boot: improve display init consistency 2019-05-09 10:17:02 -07:00
Michael Scire
cf6b9de370 boot: correct typo in MipiCal03 config 2019-05-09 09:06:36 -07:00
Michael Scire
80c380e61e boot: fix missing write in LCD vendor 0xF20 init 2019-05-09 08:15:12 -07:00
Michael Scire
967fa456ba boot: fix trailing whitespace 2019-05-09 05:03:35 -07:00
Michael Scire
6ae6c80c25 fusee: enable boot sysmodule. 2019-05-09 03:23:26 -07:00
Michael Scire
142df74694 boot: remove boot_old legacy code 2019-05-09 03:22:40 -07:00
Michael Scire
00db1dc286 boot: fix abort on < 3.0.0 2019-05-09 03:20:06 -07:00
Michael Scire
1456246f60 boot: implement PmicDriver::ShutdownSystem 2019-05-09 02:45:31 -07:00
Michael Scire
7581306109 boot: implement CheckAndRepairBootImages 2019-05-09 01:17:56 -07:00
Michael Scire
a4ee4d20ad boot: use correct clock out setting on < 6.0 2019-05-08 06:20:36 -07:00
Michael Scire
625ac5b357 boot: implement wake event configuration 2019-05-07 10:02:53 -07:00
Michael Scire
3f75a92fd2 boot: implement pmc wake config init (todo: events) 2019-05-07 09:32:37 -07:00
Michael Scire
b5e91ff9a4 boot: fix some silly mistakes 2019-05-07 01:18:54 -07:00
Michael Scire
db47a0c041 boot: implement SetFanEnabled 2019-05-06 23:14:06 -07:00
Michael Scire
5e7b33cabd 20ms, not 2s 2019-05-06 23:09:13 -07:00
Michael Scire
3cc79f4e11 boot: finish implementing CheckBatteryCharge 2019-05-06 23:08:28 -07:00
Michael Scire
7c36a827da boot: implement bc24193 driver, part of CheckBatteryCharge 2019-05-06 15:53:29 -07:00
Michael Scire
ea90325535 boot: remove debug logic for showing battery icons 2019-05-06 13:34:44 -07:00
Michael Scire
72377c2345 boot: implement battery icon drawing functions 2019-05-06 13:33:54 -07:00
Michael Scire
cccef3b85c boot: Implement battery driver setup. 2019-05-06 08:00:22 -07:00
Michael Scire
e10c6a3217 boot: add battery calibration accessors. 2019-05-06 05:59:27 -07:00
Michael Scire
4ef7b83e34 boot: implement ConfigurePinmux 2019-05-03 06:20:50 -07:00
Michael Scire
4ca53e2ef1 boot: implement SetInitialClockConfiguration 2019-05-03 05:18:36 -07:00
Michael Scire
d9da531b41 boot: implement ShowSplashScreen/Display 2019-05-03 05:00:03 -07:00
Michael Scire
93fb060fac boot: Implement DetectBootReason 2019-05-02 19:33:12 -07:00
Michael Scire
fe0d41623c boot: i2c driver fixes 2019-05-02 19:32:03 -07:00
Michael Scire
4ea6ce3156 boot: implement CheckClock 2019-05-02 18:10:07 -07:00
Michael Scire
520b5f6c59 boot: fix gpio configuration access errors 2019-05-02 17:56:04 -07:00
Michael Scire
505324f625 boot: functional exception handling/rebooting to payload 2019-05-02 17:55:50 -07:00
Michael Scire
9319463a6e fatal: use new I2cDevice enum 2019-05-02 17:54:05 -07:00
Michael Scire
31c4c33042 boot: finish i2c driver 2019-05-02 08:30:13 -07:00
Michael Scire
55a8154691 boot: implement I2cResourceManager 2019-05-02 07:18:05 -07:00
Michael Scire
453c05cf7c boot: implement I2cDriverSession 2019-05-02 06:36:31 -07:00
Michael Scire
4c5c78858c boot: implement I2cBusAccessor 2019-05-02 05:57:10 -07:00
Michael Scire
967613a261 boot: add i2c device configs 2019-05-02 02:53:48 -07:00
Michael Scire
348345340d fs.mitm: Implement FileStorage 2019-04-29 12:55:02 -07:00
Michael Scire
38159bdf9a boot: Implement initial gpio configuration 2019-04-29 09:43:48 -07:00
Michael Scire
e58948a42b boot: implement voltage change 2019-04-29 07:22:49 -07:00
Michael Scire
9c53c0c0cc boot: make our tasks explicit. 2019-04-29 06:09:47 -07:00
Michael Scire
7c5dc61795 boot: prepare for rewrite of boot sysmodule 2019-04-29 03:25:24 -07:00
Michael Scire
6034beb084 boot_100.kip vs boot_200.kip is gross 2019-04-29 03:00:04 -07:00
258 changed files with 19963 additions and 3345 deletions

View File

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

View File

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

View File

@@ -19,7 +19,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 8
#define ATMOSPHERE_RELEASE_VERSION_MICRO 9
#define ATMOSPHERE_RELEASE_VERSION_MICRO 10
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 8
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0

View File

@@ -1,4 +1,20 @@
# 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.

View File

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

View File

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

View File

@@ -37,6 +37,7 @@
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) {
@@ -163,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) {
@@ -213,8 +218,10 @@ 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;

View File

@@ -60,6 +60,7 @@ 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);

View File

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

View File

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

View File

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

View File

@@ -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
@@ -97,7 +92,7 @@ typedef struct {
uint32_t num_handlers;
} smc_table_t;
static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
static smc_table_entry_t g_smc_user_table[] = {
{0, 4, NULL},
{0xC3000401, 4, smc_set_config_user},
{0xC3000002, 1, smc_get_config_user},
@@ -118,8 +113,9 @@ static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
{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] = {
static smc_table_entry_t g_smc_priv_table[] = {
{0, 4, NULL},
{0xC4000001, 4, smc_cpu_suspend},
{0x84000002, 4, smc_cpu_off},
@@ -130,12 +126,15 @@ static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = {
{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] = {
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 */

View File

@@ -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;
}
@@ -1353,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. */
@@ -1366,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)
{

View File

@@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot_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 \

View File

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

View File

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

View File

@@ -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;
@@ -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. */

View File

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

View File

@@ -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;
}
@@ -1353,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. */
@@ -1366,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)
{

View File

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

View File

@@ -33,8 +33,7 @@
#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
@@ -47,18 +46,14 @@ 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_spl_enabled = true;
static bool g_stratosphere_boot_enabled = false;
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[], spl_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, spl_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;
@@ -66,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. */

152
mesosphere/Makefile Normal file
View 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
View 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.

View 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

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

View 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

View 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 :=

View 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

View 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

View 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

View 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

View 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

View File

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

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

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

View 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

View File

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

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

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

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

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

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

View File

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

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

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

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

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

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

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

View File

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

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

View File

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

View File

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

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

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

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

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

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

View File

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

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

View File

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

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

View File

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

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

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

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

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

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

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

View File

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

View File

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

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

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

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

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

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

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

View File

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

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

View File

@@ -0,0 +1,323 @@
#pragma once
#include <iterator>
#include <boost/intrusive/list.hpp>
#include <mesosphere/core/util.hpp>
namespace mesosphere
{
template<uint depth_, typename IntrusiveListType_, typename PrioGetterType_>
class KMultiLevelQueue {
static_assert(depth_ <= 64, "Bitfield must be constrained in a u64");
public:
static constexpr uint depth = depth_;
using IntrusiveListType = IntrusiveListType_;
using PrioGetterType = PrioGetterType_;
using value_traits = typename IntrusiveListType::value_traits;
using pointer = typename IntrusiveListType::pointer;
using const_pointer = typename IntrusiveListType::const_pointer;
using value_type = typename IntrusiveListType::value_type;
using reference = typename IntrusiveListType::reference;
using const_reference = typename IntrusiveListType::const_reference;
using difference_type = typename IntrusiveListType::difference_type;
using size_type = typename IntrusiveListType::size_type;
template<bool isConst>
class iterator_impl {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename KMultiLevelQueue::value_type;
using difference_type = typename KMultiLevelQueue::difference_type;
using pointer = typename std::conditional<
isConst,
typename KMultiLevelQueue::const_pointer,
typename KMultiLevelQueue::pointer>::type;
using reference = typename std::conditional<
isConst,
typename KMultiLevelQueue::const_reference,
typename KMultiLevelQueue::reference>::type;
bool operator==(const iterator_impl &other) const {
return (isEnd() && other.isEnd()) || (it == other.it);
}
bool operator!=(const iterator_impl &other) const {
return !(*this == other);
}
reference operator*() {
return *it;
}
pointer operator->() {
return it.operator->();
}
iterator_impl &operator++() {
if (isEnd()) {
return *this;
} else {
++it;
}
if (it == getEndItForPrio()) {
u64 prios = mlq.usedPriorities;
prios &= ~((1ull << (currentPrio + 1)) - 1);
if (prios == 0) {
currentPrio = KMultiLevelQueue::depth;
} else {
currentPrio = __builtin_ffsll(prios) - 1;
it = getBeginItForPrio();
}
}
return *this;
}
iterator_impl &operator--() {
if (isEnd()) {
if (mlq.usedPriorities != 0) {
currentPrio = 63 - __builtin_clzll(mlq.usedPriorities);
it = getEndItForPrio();
--it;
}
} else if (it == getBeginItForPrio()) {
u64 prios = mlq.usedPriorities;
prios &= (1ull << currentPrio) - 1;
if (prios != 0) {
currentPrio = __builtin_ffsll(prios) - 1;
it = getEndItForPrio();
--it;
}
} else {
--it;
}
return *this;
}
iterator_impl &operator++(int) {
const iterator_impl v{*this};
++(*this);
return v;
}
iterator_impl &operator--(int) {
const iterator_impl v{*this};
--(*this);
return v;
}
// allow implicit const->non-const
iterator_impl(const iterator_impl<false> &other)
: mlq(other.mlq), it(other.it), currentPrio(other.currentPrio) {}
friend class iterator_impl<true>;
iterator_impl() = default;
private:
friend class KMultiLevelQueue;
using container_ref = typename std::conditional<
isConst,
const KMultiLevelQueue &,
KMultiLevelQueue &>::type;
using list_iterator = typename std::conditional<
isConst,
typename IntrusiveListType::const_iterator,
typename IntrusiveListType::iterator>::type;
container_ref mlq;
list_iterator it;
uint currentPrio;
explicit iterator_impl(container_ref mlq, list_iterator const &it, uint currentPrio)
: mlq(mlq), it(it), currentPrio(currentPrio) {}
explicit iterator_impl(container_ref mlq, uint currentPrio)
: mlq(mlq), it(), currentPrio(currentPrio) {}
constexpr bool isEnd() const {
return currentPrio == KMultiLevelQueue::depth;
}
list_iterator getBeginItForPrio() const {
if constexpr (isConst) {
return mlq.levels[currentPrio].cbegin();
} else {
return mlq.levels[currentPrio].begin();
}
}
list_iterator getEndItForPrio() const {
if constexpr (isConst) {
return mlq.levels[currentPrio].cend();
} else {
return mlq.levels[currentPrio].end();
}
}
};
using iterator = iterator_impl<false>;
using const_iterator = iterator_impl<true>;
void add(reference r) {
uint prio = prioGetter(r);
levels[prio].push_back(r);
usedPriorities |= 1ul << prio;
}
void remove(const_reference r) {
uint prio = prioGetter(r);
levels[prio].erase(levels[prio].iterator_to(r));
if (levels[prio].empty()) {
usedPriorities &= ~(1ul << prio);
}
}
void remove(const_iterator it) {
remove(*it);
}
void erase(const_iterator it) {
remove(it);
}
void adjust(const_reference r, uint oldPrio, bool isCurrentThread = false) {
uint prio = prioGetter(r);
// The thread is the current thread if and only if it is first on the running queue of highest priority, so it needs to be first on the dst queue as well.
auto newnext = isCurrentThread ? levels[prio].cbegin() : levels[prio].cend();
levels[prio].splice(newnext, levels[oldPrio], levels[oldPrio].iterator_to(r));
usedPriorities |= 1ul << prio;
}
void adjust(const_iterator it, uint oldPrio, bool isCurrentThread = false) {
adjust(*it, oldPrio, isCurrentThread);
}
void transferToFront(const_reference r, KMultiLevelQueue &other) {
uint prio = prioGetter(r);
other.levels[prio].splice(other.levels[prio].begin(), levels[prio], levels[prio].iterator_to(r));
other.usedPriorities |= 1ul << prio;
if (levels[prio].empty()) {
usedPriorities &= ~(1ul << prio);
}
}
void transferToFront(const_iterator it, KMultiLevelQueue &other) {
transferToFront(*it, other);
}
void transferToBack(const_reference r, KMultiLevelQueue &other) {
uint prio = prioGetter(r);
other.levels[prio].splice(other.levels[prio].end(), levels[prio], levels[prio].iterator_to(r));
other.usedPriorities |= 1ul << prio;
if (levels[prio].empty()) {
usedPriorities &= ~(1ul << prio);
}
}
void transferToBack(const_iterator it, KMultiLevelQueue &other) {
transferToBack(*it, other);
}
void yield(uint prio, size_type n = 1) {
levels[prio].shift_forward(n);
}
void yield(const_reference r) {
uint prio = prioGetter(r);
if (&r == &levels[prio].front()) {
yield(prio, 1);
}
}
uint highestPrioritySet(uint maxPrio = 0) {
u64 priorities = maxPrio == 0 ? usedPriorities : (usedPriorities & ~((1 << maxPrio) - 1));
return priorities == 0 ? depth : (uint)(__builtin_ffsll((long long)priorities) - 1);
}
uint lowestPrioritySet(uint minPrio = depth - 1) {
u64 priorities = minPrio >= depth - 1 ? usedPriorities : (usedPriorities & ((1 << (minPrio + 1)) - 1));
return priorities == 0 ? depth : 63 - __builtin_clzll(priorities);
}
size_type size(uint prio) const {
return levels[prio].size();
}
bool empty(uint prio) const {
return (usedPriorities & (1 << prio)) == 0;
}
size_type size() const {
u64 prios = usedPriorities;
size_type sz = 0;
while (prios != 0) {
int ffs = __builtin_ffsll(prios);
sz += size((uint)ffs - 1);
prios &= ~(1ull << (ffs - 1));
}
return sz;
}
bool empty() const {
return usedPriorities == 0;
}
reference front(uint maxPrio = 0) {
// Undefined behavior if empty
uint priority = highestPrioritySet(maxPrio);
return levels[priority == depth ? 0 : priority].front();
}
const_reference front(uint maxPrio = 0) const {
// Undefined behavior if empty
uint priority = highestPrioritySet(maxPrio);
return levels[priority == depth ? 0 : priority].front();
}
reference back(uint minPrio = depth - 1) {
// Inclusive
// Undefined behavior if empty
uint priority = highestPrioritySet(minPrio); // intended
return levels[priority == depth ? 63 : priority].back();
}
const_reference back(uint minPrio = KMultiLevelQueue::depth - 1) const {
// Inclusive
// Undefined behavior if empty
uint priority = highestPrioritySet(minPrio); // intended
return levels[priority == depth ? 63 : priority].back();
}
const_iterator cbegin(uint maxPrio = 0) const {
uint priority = highestPrioritySet(maxPrio);
return priority == depth ? cend() : const_iterator{*this, levels[priority].cbegin(), priority};
}
iterator begin(uint maxPrio = 0) const {
return cbegin(maxPrio);
}
iterator begin(uint maxPrio = 0) {
uint priority = highestPrioritySet(maxPrio);
return priority == depth ? end() : iterator{*this, levels[priority].begin(), priority};
}
const_iterator cend(uint minPrio = depth - 1) const {
return minPrio == depth - 1 ? const_iterator{*this, depth} : cbegin(minPrio + 1);
}
const_iterator end(uint minPrio = depth - 1) const {
return cend(minPrio);
}
iterator end(uint minPrio = depth - 1) {
return minPrio == depth - 1 ? iterator{*this, depth} : begin(minPrio + 1);
}
void swap(KMultiLevelQueue &other)
{
std::swap(*this, other);
}
KMultiLevelQueue(PrioGetterType prioGetter) : prioGetter(prioGetter), usedPriorities(0), levels() {};
explicit KMultiLevelQueue(const value_traits &traits, PrioGetterType prioGetter = PrioGetterType{})
: prioGetter(prioGetter), usedPriorities(0), levels(detail::MakeArrayOf<IntrusiveListType, depth>(traits)) {}
private:
PrioGetterType prioGetter;
u64 usedPriorities;
std::array<IntrusiveListType, depth> levels;
};
}

View File

@@ -0,0 +1,93 @@
#pragma once
#include <chrono>
#include <atomic>
#include <mutex>
#include <mesosphere/core/util.hpp>
#include <mesosphere/core/KCoreContext.hpp>
namespace mesosphere
{
/// Fulfills Mutex requirements
class KMutex final {
public:
using native_handle_type = uiptr;
KMutex() = default;
KMutex(const KMutex &) = delete;
KMutex(KMutex &&) = delete;
KMutex &operator=(const KMutex &) = delete;
KMutex &operator=(KMutex &&) = delete;
native_handle_type native_handle() { return tag; }
bool try_lock()
{
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
return try_lock_impl_get_owner(currentThread) == nullptr;
}
void lock()
{
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
KThread *owner;
while ((owner = try_lock_impl_get_owner(currentThread)) != nullptr) {
// Our thread may be resumed even if we weren't given the mutex
lock_slow_path(*owner, *currentThread);
}
}
void unlock()
{
// Ensure sequencial ordering, to happen-after mutex load
std::atomic_thread_fence(std::memory_order_seq_cst);
KThread *currentThread = KCoreContext::GetCurrentInstance().GetCurrentThread();
native_handle_type thisThread = (native_handle_type)currentThread;
/*
If we don't have any waiter, just store 0 (free the mutex).
Otherwise, or if a race condition happens and a new waiter appears,
take the slow path.
*/
if (tag.load() != thisThread || !tag.compare_exchange_strong(thisThread, 0)) {
unlock_slow_path(*currentThread);
}
}
private:
KThread *try_lock_impl_get_owner(KThread *currentThread)
{
native_handle_type oldTag, newTag;
native_handle_type thisThread = (native_handle_type)currentThread;
oldTag = tag.load();
do {
// Add "has listener" if the mutex was not free
newTag = oldTag == 0 ? thisThread : (oldTag | 1);
} while (!tag.compare_exchange_weak(oldTag, newTag, std::memory_order_seq_cst));
// The mutex was not free or was not ours => return false
if(oldTag != 0 && (oldTag & ~1) != thisThread) {
return (KThread *)(oldTag &~ 1);
} else {
/*
Ensure sequencial ordering if mutex was acquired
and mutex lock happens-before mutex unlock.
*/
std::atomic_thread_fence(std::memory_order_seq_cst);
return nullptr;
}
}
void lock_slow_path(KThread &owner, KThread &requester);
void unlock_slow_path(KThread &owner);
std::atomic<native_handle_type> tag{};
};
}

View File

@@ -0,0 +1,137 @@
#pragma once
#include <array>
#include <mutex>
#include <mesosphere/core/util.hpp>
#include <mesosphere/threading/KMultiLevelQueue.hpp>
#include <mesosphere/threading/KCriticalSection.hpp>
#include <mesosphere/threading/KThread.hpp>
namespace mesosphere
{
class KScheduler {
public:
class Global {
public:
using MlqType = KMultiLevelQueue<64, KThread::SchedulerList, __decltype(&KThread::GetPriorityOf)>;
Global() = delete;
Global(const Global &) = delete;
Global(Global &&) = delete;
Global &operator=(const Global &) = delete;
Global &operator=(Global &&) = delete;
static MlqType &GetScheduledMlq(uint coreId) { return scheduledMlqs[coreId]; }
static MlqType &GetSuggestedMlq(uint coreId) { return suggestedMlqs[coreId]; }
static void SetThreadRunning(KThread &thread);
static void SetThreadPaused(KThread &thread);
static void AdjustThreadPriorityChanged(KThread &thread, uint oldPrio, bool isCurrentThread = false);
static void AdjustThreadAffinityChanged(KThread &thread, int oldCoreId, u64 oldAffinityMask);
static void YieldThread(KThread &thread);
static void YieldThreadAndBalanceLoad(KThread &thread);
static void YieldThreadAndWaitForLoadBalancing(KThread &thread);
static void YieldPreemptThread(KThread &currentKernelHandlerThread, uint coreId, uint maxPrio = 59);
static void SelectThreads();
static constexpr uint minRegularPriority = 2;
private:
friend class KScheduler;
static void TransferThreadToCore(KThread &thread, int coreId);
static void AskForReselectionOrMarkRedundant(KThread *currentThread, KThread *winner);
// allowSecondPass = true is only used in SelectThreads
static KThread *PickOneSuggestedThread(const std::array<KThread *, MAX_CORES> &currentThreads,
uint coreId, bool compareTime = false, bool allowSecondPass = false,
uint maxPrio = 0, uint minPrio = MlqType::depth - 1);
static bool reselectionRequired;
static std::array<MlqType, MAX_CORES> scheduledMlqs, suggestedMlqs;
template<typename F, typename ...Args>
static void ApplyReschedulingOperationImpl(F f, KThread &thread, int coreId, u64 affMask, Args&& ...args)
{
if (coreId >= 0) {
f(scheduledMlqs[coreId], thread, std::forward<Args>(args)...);
affMask &= ~(1 << coreId);
}
while (affMask != 0) {
coreId = __builtin_ffsll(affMask) - 1;
f(suggestedMlqs[coreId], thread, std::forward<Args>(args)...);
affMask &= ~(1 << coreId);
}
}
template<typename F, typename ...Args>
static void ApplyReschedulingOperation(F f, KThread &thread, Args&& ...args)
{
u64 aff = thread.GetAffinityMask();
int coreId = thread.GetCurrentCoreId();
ApplyReschedulingOperationImpl(f, thread, coreId, aff, std::forward<Args>(args)...);
thread.IncrementSchedulerOperationCount();
reselectionRequired = true;
}
};
KScheduler() = default;
KScheduler(const KScheduler &) = delete;
KScheduler(KScheduler &&) = delete;
KScheduler &operator=(const KScheduler &) = delete;
KScheduler &operator=(KScheduler &&) = delete;
static KCriticalSection &GetCriticalSection() { return criticalSection; }
static void YieldCurrentThread();
static void YieldCurrentThreadAndBalanceLoad();
static void YieldCurrentThreadAndWaitForLoadBalancing();
static void HandleCriticalSectionLeave();
friend void SchedulerHandleCriticalSectionLeave()
{
HandleCriticalSectionLeave();
}
void ForceContextSwitch() {}
void ForceContextSwitchAfterIrq() {}
void SetContextSwitchNeededForWorkQueue() { isContextSwitchNeededForWorkQueue = true; }
constexpr ulong GetIdleSelectionCount() const { return idleSelectionCount; }
constexpr bool IsActive() const { return /*isActive */ true; } // TODO
private:
bool hasContextSwitchStartedAfterIrq;
bool isActive;
bool isContextSwitchNeeded;
bool isContextSwitchNeededForWorkQueue;
uint coreId;
u64 lastContextSwitchTime;
KThread *selectedThread;
KThread *previousThread;
ulong idleSelectionCount;
void *tmpStack;
KThread *idleThread;
static KCriticalSection criticalSection;
template<typename F>
void DoYieldOperation(F f, KThread &currentThread)
{
if (!currentThread.IsSchedulerOperationRedundant())
{
criticalSection.lock();
f(currentThread);
criticalSection.unlock();
ForceContextSwitch();
}
}
};
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <mesosphere/threading/KScheduler.hpp>
namespace mesosphere
{
class KScopedCriticalSection final {
private:
std::scoped_lock<KCriticalSection> lk{KScheduler::GetCriticalSection()};
};
}

View File

@@ -0,0 +1,346 @@
#pragma once
#include <atomic>
#include <boost/intrusive/list.hpp>
#include <mesosphere/core/util.hpp>
#include <mesosphere/core/Handle.hpp>
#include <mesosphere/core/Result.hpp>
#include <mesosphere/core/KSynchronizationObject.hpp>
#include <mesosphere/interfaces/ISetAllocated.hpp>
#include <mesosphere/interfaces/IAlarmable.hpp>
#include <mesosphere/interfaces/ILimitedResource.hpp>
namespace mesosphere
{
struct LightSessionRequest;
struct KThreadContext;
struct ThreadProcessListTag;
struct ThreadWaitListTag;
struct ThreadMutexWaitListTag;
using ThreadProcessListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadProcessListTag> >;
using ThreadWaitListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadWaitListTag> >;
using ThreadMutexWaitListBaseHook = boost::intrusive::list_base_hook<boost::intrusive::tag<ThreadMutexWaitListTag> >;
class KThread final :
public KAutoObject,
public ILimitedResource<KThread>,
public ISetAllocated<KThread>,
public IAlarmable,
public ThreadProcessListBaseHook,
public ThreadWaitListBaseHook,
public ThreadMutexWaitListBaseHook
{
public:
MESOSPHERE_AUTO_OBJECT_TRAITS(AutoObject, Thread);
MESOSPHERE_LIMITED_RESOURCE_TRAITS(100ms);
class StackParameters final {
public:
StackParameters(std::array<u64, 4> svcPermissionMask, KThreadContext *threadCtx) :
svcPermissionMask{svcPermissionMask}, threadCtx{threadCtx} {}
void Initialize(std::array<u64, 4> svcPermissionMask, KThreadContext *threadCtx)
{
*this = StackParameters{svcPermissionMask, threadCtx};
}
constexpr bool IsExecutingSvc() const { return isExecutingSvc; }
constexpr u8 GetCurrentSvcId() const { return currentSvcId; }
constexpr uint GetInterruptBottomHalfLockCount() const { return interruptBottomHalfLockCount; }
void IncrementInterruptBottomHalfLockCount() { ++interruptBottomHalfLockCount; }
void DecrementInterruptBottomHalfLockCount() { --interruptBottomHalfLockCount; }
KThreadContext *GetThreadContext() const { return threadCtx; }
private:
std::array<u64, 4> svcPermissionMask[256/64]{};
u8 stateFlags = 0;
u8 currentSvcId = 0;
bool isExecutingSvc = false;
bool isNotStarted = true;
uint interruptBottomHalfLockCount = 1;
KThreadContext *threadCtx = nullptr;
};
struct SchedulerValueTraits {
using node_traits = boost::intrusive::list_node_traits<KThread *>;
using node = node_traits::node;
using node_ptr = node *;
using const_node_ptr = const node *;
using value_type = KThread;
using pointer = KThread *;
using const_pointer = const KThread *;
static constexpr boost::intrusive::link_mode_type link_mode = boost::intrusive::normal_link;
constexpr SchedulerValueTraits(uint coreId) : coreId(coreId) {}
node_ptr to_node_ptr (value_type &value) const {
return &value.schedulerNodes[coreId];
}
const_node_ptr to_node_ptr (const value_type &value) const {
return &value.schedulerNodes[coreId];
}
pointer to_value_ptr(node_ptr n) const {
return detail::GetParentFromArrayMember(n, coreId, &KThread::schedulerNodes);
}
const_pointer to_value_ptr(const_node_ptr n) const {
return detail::GetParentFromArrayMember(n, coreId, &KThread::schedulerNodes);
}
private:
uint coreId;
};
enum class SchedulingStatus : u16 {
Paused = 1,
Running = 2,
Exited = 3,
};
enum class ForcePauseReason : u16 {
ThreadActivity = 0,
ProcessActivity = 1,
Debug = 2,
Reserved = 3,
KernelLoading = 4,
};
using ProcessList = typename boost::intrusive::make_list<
KThread,
boost::intrusive::base_hook<ThreadProcessListBaseHook>
>::type;
using SchedulerList = typename boost::intrusive::make_list<
KThread,
boost::intrusive::value_traits<KThread::SchedulerValueTraits>
>::type;
using WaitList = typename boost::intrusive::make_list<
KThread,
boost::intrusive::base_hook<ThreadWaitListBaseHook>,
boost::intrusive::constant_time_size<false>
>::type;
private:
using MutexWaitList = typename boost::intrusive::make_list<
KThread,
boost::intrusive::base_hook<ThreadMutexWaitListBaseHook>
>::type;
public:
virtual void OnAlarm() override;
static constexpr uint GetPriorityOf(const KThread &thread)
{
return thread.priority;
}
constexpr uint GetPriority() const { return priority; }
constexpr u64 GetId() const { return id; }
constexpr int GetCurrentCoreId() const { return currentCoreId; }
constexpr ulong GetAffinityMask() const { return affinityMask; }
constexpr long GetLastScheduledTime() const { return lastScheduledTime; }
StackParameters &GetStackParameters()
{
return *(StackParameters *)(kernelStackTop - sizeof(StackParameters));
}
KProcess *GetOwner() const { return owner; }
bool IsSchedulerOperationRedundant() const;
void IncrementSchedulerOperationCount();
void SetRedundantSchedulerOperation();
void SetCurrentCoreId(int coreId) { currentCoreId = coreId; }
void SetProcessLastThreadAndIdleSelectionCount(ulong idleSelectionCount);
void UpdateLastScheduledTime() { ++lastScheduledTime; /* FIXME */}
constexpr SchedulingStatus GetSchedulingStatus() const
{
return (SchedulingStatus)(currentSchedMaskFull & 0xF);
}
constexpr bool IsForcePausedFor(ForcePauseReason reason) const
{
return (schedMaskForForcePauseFull & (1 << (4 + ((ushort)reason)))) != 0;
}
constexpr bool IsForcePaused() const
{
return (schedMaskForForcePauseFull & ~0xF) != 0;
}
static constexpr bool CompareSchedulingStatusFull(ushort fullMask, SchedulingStatus status)
{
return fullMask == (ushort)status;
}
constexpr bool CompareSchedulingStatusFull(SchedulingStatus status) const
{
return CompareSchedulingStatusFull(schedMaskForForcePauseFull, status);
}
/// Returns old full mask
ushort SetSchedulingStatusField(SchedulingStatus status)
{
ushort oldMaskFull = currentSchedMaskFull;
currentSchedMaskFull = (currentSchedMaskFull & ~0xF) | ((ushort)status & 0xF);
return oldMaskFull;
}
void AddForcePauseReasonToField(ForcePauseReason reason)
{
schedMaskForForcePauseFull |= 1 << (4 + ((ushort)reason));
}
void RemoveForcePauseReasonToField(ForcePauseReason reason)
{
schedMaskForForcePauseFull |= ~(1 << (4 + ((ushort)reason)));
}
ushort CommitForcePauseToField()
{
ushort oldMaskFull = currentSchedMaskFull;
currentSchedMaskFull = (schedMaskForForcePauseFull & ~0xF) | (currentSchedMaskFull & 0xF);
return oldMaskFull;
}
ushort RevertForcePauseToField()
{
ushort oldMaskFull = currentSchedMaskFull;
currentSchedMaskFull &= 0xF;
return oldMaskFull;
}
void AdjustScheduling(ushort oldMaskFull);
void Reschedule(SchedulingStatus newStatus);
/// Sets status regardless of force-pausing.
void RescheduleIfStatusEquals(SchedulingStatus expectedStatus, SchedulingStatus newStatus);
void AddForcePauseReason(ForcePauseReason reason);
void RemoveForcePauseReason(ForcePauseReason reason);
bool IsDying() const
{
// Or already dead
/*
terminationWanted is only set on exit, under scheduler critical section, to true,
and the readers are either a thread under critical section (most common), or end-of-irq/svc/other exception,
therefore synchronization outside critsec can be implemented through fences, I think
*/
return CompareSchedulingStatusFull(SchedulingStatus::Exited) || terminationWanted;
}
void SetTerminationWanted()
{
terminationWanted = true;
std::atomic_thread_fence(std::memory_order_seq_cst);
}
/// Takes effect when critical section is left
bool WaitForKernelSync(WaitList &waitList);
/// Takes effect when critical section is left
void ResumeFromKernelSync();
/// Takes effect when critical section is left
void ResumeFromKernelSync(Result res);
/// Takes effect when critical section is left -- all threads in waitlist
static void ResumeAllFromKernelSync(WaitList &waitList);
/// Takes effect when critical section is left -- all threads in waitlist
static void ResumeAllFromKernelSync(WaitList &waitList, Result res);
/// Takes effect immediately
void CancelKernelSync();
/// Takes effect immediately
void CancelKernelSync(Result res);
/// Needs to be in kernel sync
bool IsInKernelSync() const { return currentWaitList != nullptr; }
/// User sync
constexpr bool IsWaitingSync() const { return isWaitingSync; }
void SetWaitingSync(bool isWaitingSync) { this->isWaitingSync = isWaitingSync; }
constexpr bool IsSyncCancelled() const { return isSyncCancelled; }
void SetSyncCancelled(bool isSyncCancelled) { this->isSyncCancelled = isSyncCancelled; }
void ClearSync()
{
signaledSyncObject = nullptr;
syncResult = ResultSuccess();
}
constexpr Result GetSyncResult() const { return syncResult; }
/// Takes effect when critical section is left
void HandleSyncObjectSignaled(KSynchronizationObject *syncObj);
LightSessionRequest *GetCurrentLightSessionRequest() const { return currentLightSessionRequest; }
void SetCurrentLightSessionRequest(LightSessionRequest *currentLightSessionRequest)
{
this->currentLightSessionRequest = currentLightSessionRequest;
}
template<typename Clock, typename Duration>
Result WaitSynchronization(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const std::chrono::time_point<Clock, Duration> &timeoutTime)
{
return WaitSynchronizationImpl(outId, syncObjs, numSyncObjs, timeoutTime);
}
constexpr size_t GetNumberOfKMutexWaiters() const { return numKernelMutexWaiters; }
constexpr uiptr GetWantedMutex() const { return wantedMutex; }
void SetWantedMutex(uiptr mtx) { wantedMutex = mtx; }
void AddMutexWaiter(KThread &waiter);
KThread *RelinquishMutex(size_t *count, uiptr mutexAddr);
void RemoveMutexWaiter(KThread &waiter);
void InheritDynamicPriority();
KThread() = default;
KThread(KProcess *owner, u64 id, uint priority) : KAutoObject(), owner(owner), schedulerNodes(),
id(id), basePriority(priority), priority(priority),
currentCoreId(0), affinityMask(15) {};
friend void IncrementThreadInterruptBottomHalfLockCount(KThread &thread)
{
thread.GetStackParameters().IncrementInterruptBottomHalfLockCount();
}
friend void DecrementThreadInterruptBottomHalfLockCount(KThread &thread)
{
thread.GetStackParameters().DecrementInterruptBottomHalfLockCount();
}
private:
Result WaitSynchronizationImpl(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const KSystemClock::time_point &timeoutTime);
void AddToMutexWaitList(KThread &thread);
MutexWaitList::iterator RemoveFromMutexWaitList(MutexWaitList::const_iterator it);
void RemoveFromMutexWaitList(const KThread &t);
KProcess *owner = nullptr;
boost::intrusive::list_node_traits<KThread *>::node schedulerNodes[4]{};
WaitList *currentWaitList = nullptr;
u64 id = 0;
long redundantSchedulerOperationCount = 0;
ushort currentSchedMaskFull = (ushort)SchedulingStatus::Paused;
ushort schedMaskForForcePauseFull = 0;
bool terminationWanted = false;
uint basePriority = 64, priority = 64;
int currentCoreId = -1;
ulong affinityMask = 0;
bool isSyncCancelled = false;
bool isWaitingSync = false;
LightSessionRequest *currentLightSessionRequest = nullptr; // located in kernel thread stacks
uiptr wantedMutex = 0;
KThread *wantedMutexOwner = nullptr;
MutexWaitList mutexWaitList{};
size_t numKernelMutexWaiters = 0;
uiptr kernelStackTop = 0;
KSynchronizationObject *signaledSyncObject = nullptr;
Result syncResult = ResultSuccess();
u64 lastScheduledTime = 0;
};
MESOSPHERE_AUTO_OBJECT_DEFINE_INCREF(Thread);
}

View File

@@ -0,0 +1,10 @@
#include <mesosphere/core/KAutoObject.hpp>
namespace mesosphere
{
KAutoObject::~KAutoObject()
{
}
}

View File

@@ -0,0 +1,11 @@
#include <mesosphere/core/KCoreContext.hpp>
#include <mesosphere/threading/KScheduler.hpp>
namespace mesosphere
{
static KScheduler scheds[4];
std::array<KCoreContext, MAX_CORES> KCoreContext::instances{ &scheds[0], &scheds[1], &scheds[2], &scheds[3] };
}

View File

@@ -0,0 +1,79 @@
#include <mesosphere/core/KObjectRegistry.hpp>
#include <utility>
namespace mesosphere
{
KObjectRegistry KObjectRegistry::instance{};
const KObjectRegistry::Node *KObjectRegistry::FindImpl(const char *name) const
{
auto it = std::find_if(
nameNodes.cbegin(),
nameNodes.cend(),
[name](const Node &nd) {
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
}
);
return it == nameNodes.cend() ? nullptr : &*it;
}
KObjectRegistry::Node *KObjectRegistry::FindImpl(const char *name)
{
auto it = std::find_if(
nameNodes.begin(),
nameNodes.end(),
[name](const Node &nd) {
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
}
);
return it == nameNodes.end() ? nullptr : &*it;
}
SharedPtr<KAutoObject> KObjectRegistry::Find(const char *name) const
{
std::scoped_lock guard{mutex};
const Node *node = FindImpl(name);
return node == nullptr ? nullptr : node->obj;
}
Result KObjectRegistry::Register(SharedPtr<KAutoObject> obj, const char *name)
{
std::scoped_lock guard{mutex};
Node *node = FindImpl(name);
if (node != nullptr) {
// Name already exists, just replace the auto object.
node->obj = std::move(obj);
return ResultSuccess();
} else {
if (nameNodes.emplace_back_or_fail(std::move(obj), name) == nullptr) {
return ResultKernelInvalidState();
}
return ResultSuccess();
}
}
Result KObjectRegistry::Unregister(const char *name)
{
std::scoped_lock guard{mutex};
auto it = std::find_if(
nameNodes.cbegin(),
nameNodes.cend(),
[name](const Node &nd) {
return std::strncmp(name, nd.name, sizeof(nd.name)) == 0;
}
);
if (it == nameNodes.cend()) {
return ResultKernelNotFound();
} else {
nameNodes.erase(it);
return ResultSuccess();
}
}
}

View File

@@ -0,0 +1,36 @@
#include <mesosphere/core/KSynchronizationObject.hpp>
#include <mesosphere/core/Result.hpp>
#include <mesosphere/threading/KScopedCriticalSection.hpp>
#include <mesosphere/threading/KThread.hpp>
#include <mutex>
namespace mesosphere
{
KSynchronizationObject::~KSynchronizationObject()
{
}
void KSynchronizationObject::NotifyWaiters()
{
KScopedCriticalSection criticalSection;
if (IsSignaled()) {
for (auto &&waiter : waiters) {
waiter->HandleSyncObjectSignaled(this);
}
}
}
KLinkedList<KThread *>::const_iterator KSynchronizationObject::AddWaiter(KThread &thread)
{
return waiters.insert(waiters.end(), &thread);
}
void KSynchronizationObject::RemoveWaiter(KLinkedList<KThread *>::const_iterator it)
{
waiters.erase(it);
}
}

View File

@@ -0,0 +1,20 @@
#include <mesosphere/interfaces/IAlarmable.hpp>
#include <mesosphere/core/KCoreContext.hpp>
#include <mesosphere/interrupts/KAlarm.hpp>
namespace mesosphere
{
void IAlarmable::SetAlarmTimeImpl(const KSystemClock::time_point &alarmTime)
{
this->alarmTime = alarmTime;
KCoreContext::GetCurrentInstance().GetAlarm()->AddAlarmable(*this);
}
void IAlarmable::ClearAlarm()
{
KCoreContext::GetCurrentInstance().GetAlarm()->RemoveAlarmable(*this);
alarmTime = KSystemClock::time_point{};
}
}

View File

@@ -0,0 +1,12 @@
#include <mesosphere/interfaces/IInterruptibleWork.hpp>
namespace mesosphere
{
IWork *IInterruptibleWork::HandleInterrupt(uint interruptId)
{
(void)interruptId;
return (IWork *)this;
}
}

View File

@@ -0,0 +1,26 @@
#include <mesosphere/interfaces/ILimitedResource.hpp>
#include <mesosphere/processes/KProcess.hpp>
#include <mesosphere/kresources/KResourceLimit.hpp>
namespace mesosphere::detail
{
void ReleaseResource(const SharedPtr<KResourceLimit> &reslimit, KAutoObject::TypeId typeId, size_t count, size_t realCount)
{
if (reslimit != nullptr) {
reslimit->Release(KResourceLimit::GetCategory(typeId), count, realCount);
} else {
KResourceLimit::GetDefaultInstance().Release(KResourceLimit::GetCategory(typeId), count, realCount);
}
}
void ReleaseResource(const SharedPtr<KProcess> &owner, KAutoObject::TypeId typeId, size_t count, size_t realCount)
{
if (owner != nullptr) {
return ReleaseResource(owner->GetResourceLimit(), typeId, count, realCount);
} else {
KResourceLimit::GetDefaultInstance().Release(KResourceLimit::GetCategory(typeId), count, realCount);
}
}
}

View File

@@ -0,0 +1,56 @@
#include <mesosphere/interrupts/KAlarm.hpp>
#include <mesosphere/threading/KScopedCriticalSection.hpp>
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
namespace mesosphere
{
void KAlarm::AddAlarmable(IAlarmable &alarmable)
{
std::scoped_lock guard{spinlock};
alarmables.insert(alarmable);
KSystemClock::SetAlarm(alarmables.cbegin()->GetAlarmTime());
}
void KAlarm::RemoveAlarmable(const IAlarmable &alarmable)
{
std::scoped_lock guard{spinlock};
alarmables.erase(alarmable);
KSystemClock::SetAlarm(alarmables.cbegin()->GetAlarmTime());
}
void KAlarm::HandleAlarm()
{
{
KScopedCriticalSection critsec{};
std::scoped_lock guard{spinlock};
KSystemClock::SetInterruptMasked(true); // mask timer interrupt
KSystemClock::time_point currentTime = KSystemClock::now(), maxAlarmTime;
while (alarmables.begin() != alarmables.end()) {
IAlarmable &a = *alarmables.begin();
maxAlarmTime = a.alarmTime;
if (maxAlarmTime > currentTime) {
break;
}
alarmables.erase(a);
a.alarmTime = KSystemClock::time_point{};
a.OnAlarm();
}
if (maxAlarmTime > KSystemClock::time_point{}) {
KSystemClock::SetAlarm(maxAlarmTime);
}
}
{
// TODO Reenable interrupt 30
KInterruptMaskGuard guard{};
}
}
}

View File

@@ -0,0 +1,19 @@
#include <mesosphere/interrupts/KInterruptEvent.hpp>
namespace mesosphere
{
KInterruptEvent::~KInterruptEvent()
{
// delete receiver?
}
Result KInterruptEvent::Initialize(int irqId, u32 flags)
{
// TODO implement
(void)flags;
this->irqId = irqId;
return ResultSuccess();
}
}

View File

@@ -0,0 +1,45 @@
#include <mesosphere/interrupts/KWorkQueue.hpp>
#include <mesosphere/core/KCoreContext.hpp>
#include <mesosphere/threading/KScheduler.hpp>
#include <mesosphere/arch/KInterruptMaskGuard.hpp>
namespace mesosphere
{
void KWorkQueue::AddWork(IWork &work)
{
workQueue.push_back(work);
KCoreContext::GetCurrentInstance().GetScheduler()->SetContextSwitchNeededForWorkQueue();
}
void KWorkQueue::Initialize()
{
//handlerThread.reset(new KThread); //TODO!
kassert(handlerThread == nullptr);
}
void KWorkQueue::HandleWorkQueue()
{
KCoreContext &cctx = KCoreContext::GetCurrentInstance();
while (true) {
IWork *work = nullptr;
do {
KInterruptMaskGuard imguard{};
auto it = workQueue.begin();
if (it != workQueue.end()) {
work = &*it;
workQueue.erase(it);
} else {
{
//TODO: thread usercontext scheduler/bottom hard guard
cctx.GetCurrentThread()->Reschedule(KThread::SchedulingStatus::Paused);
}
cctx.GetScheduler()->ForceContextSwitch();
}
} while (work == nullptr);
work->DoWork();
}
}
}

View File

@@ -0,0 +1,81 @@
#include <mesosphere/kresources/KResourceLimit.hpp>
namespace mesosphere
{
KResourceLimit KResourceLimit::defaultInstance{};
size_t KResourceLimit::GetCurrentValue(KResourceLimit::Category category) const
{
// Caller should check category
std::scoped_lock guard{condvar.mutex()};
return currentValues[(uint)category];
}
size_t KResourceLimit::GetLimitValue(KResourceLimit::Category category) const
{
// Caller should check category
std::scoped_lock guard{condvar.mutex()};
return limitValues[(uint)category];
}
size_t KResourceLimit::GetRemainingValue(KResourceLimit::Category category) const
{
// Caller should check category
std::scoped_lock guard{condvar.mutex()};
return limitValues[(uint)category] - currentValues[(uint)category];
}
bool KResourceLimit::SetLimitValue(KResourceLimit::Category category, size_t value)
{
std::scoped_lock guard{condvar.mutex()};
if ((long)value < 0 || currentValues[(uint)category] > value) {
return false;
} else {
limitValues[(uint)category] = value;
return true;
}
}
void KResourceLimit::Release(KResourceLimit::Category category, size_t count, size_t realCount)
{
// Caller should ensure parameters are correct
std::scoped_lock guard{condvar.mutex()};
currentValues[(uint)category] -= count;
realValues[(uint)category] -= realCount;
condvar.notify_all();
}
bool KResourceLimit::ReserveDetail(KResourceLimit::Category category, size_t count, const KSystemClock::time_point &timeoutTime)
{
std::scoped_lock guard{condvar.mutex()};
if ((long)count <= 0 || realValues[(uint)category] >= limitValues[(uint)category]) {
return false;
}
size_t newCur = currentValues[(uint)category] + count;
bool ok = false;
auto condition =
[=, &newCur] {
newCur = this->currentValues[(uint)category] + count;
size_t lval = this->limitValues[(uint)category];
return this->realValues[(uint)category] <= lval && newCur <= lval; // need to check here
};
if (timeoutTime <= KSystemClock::never) {
ok = true;
condvar.wait(condition);
} else {
ok = condvar.wait_until(timeoutTime, condition);
}
if (ok) {
currentValues[(uint)category] += count;
realValues[(uint)category] += count;
}
return ok;
}
}

1141
mesosphere/source/my_libc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
#include <cstddef>
void *operator new(std::size_t) { for(;;); }
void *operator new[](std::size_t) { for(;;); }
void operator delete(void *) { }
void operator delete(void *, std::size_t) { }
void operator delete[](void *) { }
void operator delete[](void *, std::size_t) { }
extern "C" {
int
__cxa_atexit (void (*fn) (void *),
void *arg,
void *d)
{
return 0;
}
void *__dso_handle = 0;
[[noreturn]] void __cxa_pure_virtual() { for (;;); }
}

View File

@@ -0,0 +1,44 @@
#include <mesosphere/processes/KPort.hpp>
#include <mesosphere/threading/KScopedCriticalSection.hpp>
#include <mesosphere/threading/KThread.hpp>
#include <mesosphere/core/KCoreContext.hpp>
#include <mesosphere/core/make_object.hpp>
namespace mesosphere
{
KClientPort::~KClientPort()
{
KScopedCriticalSection critsec{};
parent->isClientAlive = false;
}
bool KClientPort::IsSignaled() const
{
return numSessions.load() < maxSessions;
}
std::tuple<Result, SharedPtr<KLightClientSession>> KClientPort::ConnectLight()
{
using RetType = std::tuple<Result, SharedPtr<KLightClientSession>>;
// Official kernel first checks reslimit then session max count. We will do the opposite.
int curCount = numSessions.load();
while (curCount < maxSessions || !numSessions.compare_exchange_weak(curCount, curCount + 1));
if (curCount >= maxSessions) {
return RetType{ResultKernelOutOfSessions(), nullptr};
}
auto [res, serverSession, clientSession] = MakeObject<KLightSession>(parent.get());
if (res.IsSuccess()) {
serverSession.detach(); // Lifetime is now managed my KServerPort session list
return RetType{ResultSuccess(), clientSession};
} else {
if (numSessions.fetch_sub(1) == maxSessions) {
NotifyWaiters();
}
return RetType{res, nullptr};
}
}
}

View File

@@ -0,0 +1,20 @@
#include <mesosphere/processes/KEvent.hpp>
#include <mesosphere/processes/KProcess.hpp>
namespace mesosphere
{
KEvent::~KEvent()
{
}
Result KEvent::Initialize()
{
SetClientServerParent();
server.client = &client;
SetResourceOwner(KCoreContext::GetCurrentInstance().GetCurrentProcess());
return ResultSuccess();
}
}

View File

@@ -0,0 +1,130 @@
#include <mutex>
#include <algorithm>
#include <mesosphere/processes/KHandleTable.hpp>
#include <mesosphere/threading/KThread.hpp>
#include <mesosphere/processes/KProcess.hpp>
namespace mesosphere
{
bool KHandleTable::IsValid(Handle handle) const
{
// Official kernel checks for nullptr, however this makes the deferred-init logic more difficult.
// We use our own, more secure, logic instead, that is, free entries are <= 0.
return handle.index < capacity && handle.id > 0 && entries[handle.index].id == handle.id;
}
SharedPtr<KAutoObject> KHandleTable::GetAutoObject(Handle handle) const
{
if (!handle.IsAliasOrFree()) {
// Note: official kernel locks the spinlock here, but we don't need to.
return nullptr;
} else {
std::scoped_lock guard{spinlock};
return IsValid(handle) ? entries[handle.index].object : nullptr;
}
}
SharedPtr<KThread> KHandleTable::GetThread(Handle handle, bool allowAlias) const
{
if (allowAlias && handle == selfThreadAlias) {
return KCoreContext::GetCurrentInstance().GetCurrentThread();
} else {
return DynamicObjectCast<KThread>(GetAutoObject(handle));
}
}
SharedPtr<KProcess> KHandleTable::GetProcess(Handle handle, bool allowAlias) const
{
if (allowAlias && handle == selfProcessAlias) {
return KCoreContext::GetCurrentInstance().GetCurrentProcess();
} else {
return DynamicObjectCast<KProcess>(GetAutoObject(handle));
}
}
bool KHandleTable::Close(Handle handle)
{
SharedPtr<KAutoObject> tmp{nullptr}; // ensure any potential dtor is called w/o the spinlock being held
if (handle.IsAliasOrFree()) {
return false;
} else {
std::scoped_lock guard{spinlock};
if (IsValid(handle)) {
entries[-firstFreeIndex].id = firstFreeIndex;
firstFreeIndex = -(s16)handle.index;
--numActive;
tmp = std::move(entries[handle.index].object);
return true;
} else {
return false;
}
}
}
std::tuple<Result, Handle> KHandleTable::Generate(SharedPtr<KAutoObject> obj)
{
// Note: nullptr is accepted, for deferred-init.
std::scoped_lock guard{spinlock};
if (numActive >= capacity) {
return {ResultKernelOutOfHandles(), Handle{}};
}
// Get/allocate the entry
u16 index = (u16)-firstFreeIndex;
Entry *e = &entries[-firstFreeIndex];
firstFreeIndex = e->id;
e->id = idCounter;
e->object = std::move(obj);
size = ++numActive > size ? numActive : size;
idCounter = idCounter == 0x7FFF ? 1 : idCounter + 1;
return {ResultSuccess(), Handle{index, e->id, false}};
}
Result KHandleTable::Set(SharedPtr<KAutoObject> obj, Handle handle)
{
if (!handle.IsAliasOrFree() && IsValid(handle)) {
std::scoped_lock guard{spinlock};
entries[handle.index].object = std::move(obj);
return ResultSuccess();
} else {
return ResultKernelInvalidHandle();
}
}
void KHandleTable::Destroy()
{
spinlock.lock();
u16 capa = capacity;
capacity = 0;
firstFreeIndex = 0;
spinlock.unlock();
for (u16 i = 0; i < capa; i++) {
entries[i].object = nullptr;
entries[i].id = -(i + 1);
}
}
/*
KHandleTable::KHandleTable(size_t capacity_) : capacity((u16)capacity_)
{
// Note: caller should check the > case, and return an error in that case!
capacity = capacity > capacityLimit || capacity == 0 ? (u16)capacityLimit : capacity;
u16 capa = capacity;
Destroy();
capacity = capa;
}*/
KHandleTable::~KHandleTable()
{
Destroy();
}
}

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