Compare commits

..

21 Commits

Author SHA1 Message Date
Michael Scire
7b6df8c2f7 boot: remove references to memalign/malloc 2021-01-19 05:23:02 -08:00
Michael Scire
3b2c2ba5a2 ams_mitm: update for new sf semantics 2021-01-19 02:34:02 -08:00
Michael Scire
88a99bc68e dmnt: update for new sf semantics 2021-01-18 18:43:36 -08:00
Michael Scire
2d7a85d14f creport: update for new sf semantics 2021-01-18 17:50:40 -08:00
Michael Scire
2801d00d98 boot2: update for new sf semantics 2021-01-18 17:35:05 -08:00
Michael Scire
57c197ec1c boot: update for new sf-semantics 2021-01-18 17:22:36 -08:00
Michael Scire
2696240566 fatal: update screen task to use native window directly 2021-01-18 16:54:55 -08:00
Michael Scire
ea9d360b14 fatal: wip (pending libnx pr) update for new sf semantics 2021-01-18 08:48:47 -08:00
Michael Scire
a63b97fbbd jpegdec: note libjpeg-turbo TODO 2021-01-18 06:35:14 -08:00
Michael Scire
6c6e698a3e libstrat: move weak HasLaunchedBootProgram to non-lto object file 2021-01-18 06:26:22 -08:00
Michael Scire
15b225f919 loader: fix failure-to-early-return in launch record management 2021-01-18 06:20:13 -08:00
Michael Scire
ac2c713ee0 util::unique_lock, update loader to new sf semantics 2021-01-18 06:18:14 -08:00
Michael Scire
6a2f8e8344 string_view: remove now unecessary comment 2021-01-18 05:44:08 -08:00
Michael Scire
54a1ba2a7e ncm: update for new sf semantics 2021-01-18 05:41:15 -08:00
Michael Scire
2c485f832c util::string_view, update pgl for new sf semantics 2021-01-18 05:20:27 -08:00
Michael Scire
6467f6f07b pm: update for new sf semantics 2021-01-17 23:12:26 -08:00
Michael Scire
4f001b4f4c erpt: update for new sf semantics 2021-01-17 22:55:39 -08:00
Michael Scire
a6e452ed9e ro: reduce memory usage by excising (unused) std::malloc 2021-01-17 22:23:19 -08:00
Michael Scire
fb6a4e28a5 ro: update for new sf semantics 2021-01-17 22:03:26 -08:00
Michael Scire
bc9da91362 sm, spl: update to use new sf semantics 2021-01-17 15:03:51 -08:00
Michael Scire
8956e3bd29 libstrat: convert to experimental new (super-accurate) sf allocation semantics 2021-01-17 07:55:32 -08:00
663 changed files with 3002 additions and 58130 deletions

2
.gitignore vendored
View File

@@ -95,5 +95,3 @@ sept/sept-secondary/KEYS.py
**/build_nintendo_nx_arm
**/build_nintendo_nx_x64
**/build_nintendo_nx_x86
stratosphere/test/

View File

@@ -1,9 +1,3 @@
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro)
endif
include $(DEVKITPRO)/devkitA64/base_tools
TOPTARGETS := all clean dist-no-debug dist
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
AMSHASH := $(shell git rev-parse --short HEAD)
@@ -62,11 +56,18 @@ dist-no-debug: all
mkdir atmosphere-$(AMSVER)/atmosphere
mkdir atmosphere-$(AMSVER)/sept
mkdir atmosphere-$(AMSVER)/switch
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates
mkdir -p atmosphere-$(AMSVER)/atmosphere/config
mkdir -p atmosphere-$(AMSVER)/atmosphere/flags
touch atmosphere-$(AMSVER)/atmosphere/flags/clean_stratosphere_for_0.19.0.flag
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin
cp fusee/fusee-secondary/fusee-secondary-experimental.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
@@ -83,27 +84,19 @@ dist-no-debug: all
cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini
cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp
cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B/exefs.nsp
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032/exefs.nsp
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000036/exefs.nsp
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp
cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C/exefs.nsp
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D/exefs.nsp
cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B/exefs.nsp
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/exefs.nsp
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034/exefs.nsp
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/exefs.nsp
cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C/exefs.nsp
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042/exefs.nsp
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags/boot2.flag
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
@@ -142,7 +135,6 @@ dist: dist-no-debug
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf

View File

@@ -1,13 +1,9 @@
[eupld]
; Disable uploading error reports to Nintendo
[eupld]
; upload_enabled = u8!0x0
[usb]
; Enable USB 3.0 superspeed for homebrew
; 0 = USB 3.0 support is system default (usually disabled), 1 = USB 3.0 support is enabled.
; usb30_force_enabled = u8!0x0
[ro]
; Control whether RO should ease its validation of NROs.
; (note: this is normally not necessary, and ips patches can be used.)
[ro]
; ease_nro_restriction = u8!0x1
; Atmosphere custom settings
[atmosphere]
@@ -36,23 +32,16 @@
; NOTE: EXPERIMENTAL
; If you do not know what you are doing, do not touch this yet.
; fsmitm_redirect_saves_to_sd = u8!0x0
; Controls whether to enable the deprecated hid mitm
; to fix compatibility with old homebrew.
; 0 = Do not enable, 1 = Enable.
; Please note this setting may be removed in a
; future release of Atmosphere.
; enable_deprecated_hid_mitm = u8!0x0
; Controls whether am sees system settings "DebugModeFlag" as
; enabled or disabled.
; 0 = Disabled (not debug mode), 1 = Enabled (debug mode)
; enable_am_debug_mode = u8!0x0
; Controls whether dns.mitm is enabled
; 0 = Disabled, 1 = Enabled
; enable_dns_mitm = u8!0x1
; Controls whether dns.mitm uses the default redirections in addition to
; whatever is specified in the user's hosts file.
; 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents)
; add_defaults_to_dns_hosts = u8!0x1
; Controls whether dns.mitm logs to the sd card for debugging
; 0 = Disabled, 1 = Enabled
; enable_dns_mitm_debug_log = u8!0x0
; Controls whether htc is enabled
; 0 = Disabled, 1 = Enabled
; enable_htc = u8!0x0
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.

View File

@@ -1,76 +1,4 @@
# Changelog
## 0.19.1
+ An issue was fixed that caused a fatal error when using official `migration` services to transfer data between consoles.
+ An issue was fixed in `ncm` that caused an error when the OS tried to enumerate installed SD card content.
+ Several issues were fixed, and usability and stability were improved.
## 0.19.0
+ Support was added for 12.0.0.
+ `mesosphère` was updated to reflect the latest official kernel behavior.
+ `sm`, `boot2`, `pgl` were updated to reflect the latest official behaviors.
+ **Please Note**: 12.0.0 added a new protocol for IPC ("tipc"), which has been freshly reimplemented in its entirety.
+ It is possible there may be as of yet unfound issues; if there are, please send the appropriate crash reports to SciresM (SciresM#0524 on discord).
+ Homebrew which uses atmosphere extensions (including the mitm API) will need to be re-compiled in order to function on 0.19.0.
+ I apologize for this, but it's unavoidable for technical reasons. If you're affected by this and mad about it, please contact SciresM to complain.
+ `erpt` was partially updated to reflect the latest official behaviors.
+ New features were added to erpt to track the activity of running applets, and to detect when a forced shutdown occurs.
+ These behaviors have been temporarily stubbed, as they are not necessary for 12.0.0 to run (and their outputs won't be saved anywhere).
+ A future atmosphère update will implement these behaviors, in the interest of reflecting official logic as faithfully as we can.
+ Atmosphère no longer uses the /contents/ folder for its own programs.
+ Atmosphère's system modules are now bundled together in the single file "stratosphere.romfs".
+ For those working on developing for atmosphère, executables inside the /contents/ directory will be preferred to those in "stratosphere.romfs".
+ **Please Note**: In order to facilitate this change (and the desired behavior), the first time you boot after extracting a release zip, atmosphère system modules inside /contents/ will be deleted.
+ This will have no impact on user programs (it only removes programs with specific program ids).
+ Improvements were made to mesosphere, including:
+ An extension InfoType was added for getting the current process handle, without having to spawn a thread and do IPC with oneself.
+ An issue was fixed in SvcSetDebugThreadContext.
+ An issue was fixed when doing IPC with user buffers.
+ Support was fixed for toggling the custom setting `usb!usb30_force_enabled` on 9.0.0+.
+ This was broken by Nintendo's introducing a dependency that made USB a requirement to launch before custom settings are parsed.
+ Since the fix, you can now toggle the setting (as you could prior to atmosphère 0.9.4), and it will work as expected.
+ **Please Note**: Enabling USB 3.0 often severely impacts wireless communications.
+ Because of this, the setting will default to off. If you experience issues with it enabled, consider disabling it.
+ A warning was added to daybreak when resetting the console to factory settings.
+ Substantial work was completed towards atmosphere's upcoming implementation of the host target connection protocol.
+ Once completed, users will be able to interact with a Switch running atmosphère via a PC application ("Starlink") currently under development.
+ Planned eventual features for connected consoles include a gdbstub, interacting with memory (for cheat development), streaming gameplay audio and video, and accessing the Switch's SD card filesystem.
+ Switch homebrew will also have access to a (configurable and sandboxed) filesystem on the host PC, while connected.
+ Towards this end, the following was accomplished:
+ The "htc" system module was reimplemented completely.
+ The system module which provides remote access to the SD card was reimplemented completely.
+ This is currently the active focus of atmosphère's development.
+ **Please Note**: Support is not yet completed, and users are disadvised from interacting with the related settings for the time being, unless they particularly know what they're doing.
+ A number of minor issues were fixed, including:
+ A bug was fixed in `dmnt` that could cause a fatal when launching certain games with cheats active.
+ An issue was fixed that could cause an abort in `sm` when using a large number of custom system modules.
+ An issue was fixed that prevented launching gamecards on 1.0.0.
+ Minor issues were fixed in the cheat virtual machine's behavior.
+ Several issues were fixed, and usability and stability were improved.
## 0.18.1
+ A number of minor issues were fixed, including:
+ The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr.
+ This fixes youtube ad-blocking, and possibly other usecases.
+ A bug was fixed that caused ams.mitm to incorrectly cache data storages.
+ This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases).
+ A bug was fixed in power state control module registration.
+ This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences.
+ A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs.
+ This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved.
+ Several issues were fixed, and usability and stability were improved.
## 0.18.0
+ A new mitm module was added (`dns.mitm`).
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.
+ By default atmosphère redirects resolution requests for official telemetry servers to a loopback address.
+ Documentation on how to configure `dns.mitm` to meet your more specific needs may be found [here](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/features/dns_mitm.md).
+ The service framework API (`sf`) was refactored to be more accurate to official logic and greatly reduce memory requirements.
+ The comparison of atmosphère module memory usage versus Nintendo's found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons) was updated to reflect this.
+ **Please Note**: If you are a developer using the libstratosphere service APIs, some updating may be required. Contact SciresM#0524 on discord for assistance if required.
+ A number of deprecations were removed, following a general codebase cleanup:
+ The `sm` extension to not unregister services on connection close was superseded by official opt-in logic in 11.0.0, and has been removed in favor of official logic.
+ This should have zero impact on users.
+ The temporary `hid-mitm` added in 0.9.0 has finally been removed, following over a year of deprecation.
+ There shouldn't be any homebrew in use still affected by this, but the situation will be monitored.
+ If this is somehow still a real issue, an unaffiliated hid mitm sysmodule providing the same functionality can be created and released, separate from atmosphère itself.
+ Several issues were fixed, and usability and stability were improved.
## 0.17.1
+ A number of atmosphère's modules were using more memory than their Nintendo equivalent's in 0.17.0; a number of code generatio tweaks have been applied to fix this across the board.
+ A detailed comparison of atmosphère module memory usage versus Nintendo's was made and can be found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons).
@@ -87,7 +15,7 @@
+ This also substantially improves power drain when the system is shut off; consoles powered off from Atmosphere should now drain battery at the same reduced rate as original firmware.
+ A number of minor changes were made, including:
+ A number of inconsistencies in the build system were fixed.
+ For those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked.
+ Fow those building atmosphère at home, the `boot` sysmodule will no longer rebuild every time make is invoked.
+ This substantially improves build times during development iteration.
+ `sm` was updated to more accurately reflect how official code manages request deferral.
+ `mesosphère` was updated to more accurately reflect official kernel management of the trace buffer.

View File

@@ -33,8 +33,3 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
### System Settings
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).

View File

@@ -1,53 +0,0 @@
# DNS.mitm
As of 0.18.0, atmosphère provides a mechanism for redirecting DNS resolution requests.
By default, atmosphère redirects resolution requests for official telemetry servers, redirecting them to a loopback address.
## Hosts files
DNS.mitm can be configured through the usage of a slightly-extended `hosts` file format, which is parsed only once on system startup.
In particular, hosts files parsed by DNS.mitm have the following extensions to the usual format:
+ `*` is treated as a wildcard character, matching any collection of 0 or more characters wherever it occurs in a hostname.
+ `%` is treated as a stand-in for the value of `nsd!environment_identifier`. This is always `lp1`, on production devices.
If multiple entries in a host file match a domain, the last-defined match is used.
Please note that homebrew may trigger a hosts file re-parse by sending the extension IPC command 65000 ("AtmosphereReloadHostsFile") to a connected `sfdnsres` session.
### Hosts file selection
Atmosphère will try to read hosts from the following file paths, in order, stopping once it successfully performs a file read:
+ (emummc only) `/atmosphere/hosts/emummc_%04lx.txt`, formatted with the emummc's id number (see `emummc.ini`).
+ (emummc only) `/atmosphere/hosts/emummc.txt`.
+ (sysmmc only) `/atmosphere/hosts/sysmmc.txt`.
+ `/atmosphere/hosts/default.txt`
If `/atmosphere/hosts/default.txt` does not exist, atmosphère will create it to contain the defaults.
### Atmosphère defaults
By default, atmosphère's default redirections are parsed **in addition to** the contents of the loaded hosts file.
This is equivalent to thinking of the loaded hosts file as having the atmosphère defaults prepended to it.
This setting is considered desirable, because it minimizes the telemetry risks if a user forgets to update a custom hosts file on a system update which changes the telemetry servers.
This behavior can be opted-out from by setting `atmosphere!add_defaults_to_dns_hosts = u8!0x0` in `system_settings.ini`.
The current default redirections are:
```
# Nintendo telemetry servers
127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net
```
## Debugging
On startup (or on hosts file re-parse), DNS.mitm will log both what hosts file it selected and the contents of all redirections it parses to `/atmosphere/logs/dns_mitm_startup.log`.
In addition, if the user sets `atmosphere!enable_dns_mitm_debug_log = u8!0x1` in `system_settings.ini`, DNS.mitm will log all requests to GetHostByName/GetAddrInfo to `/atmosphere/logs/dns_mitm_debug.log`. All redirections will be noted when they occur.
## Opting-out of DNS.mitm entirely
If you wish to disable DNS.mitm entirely, `system_settings.ini` can be edited to set `atmosphere!enable_dns_mitm = u8!0x0`.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emuMMC
branch = develop
commit = b355ee6a8f376faa615785419c7d73a8814d9d65
parent = b24784f5c13a142bd0cb5d7edb82691c71f4bd00
commit = 5eed18eb527bbaa63aee5323c26de5b0cca6d28e
parent = 021b29d2dbc8ed0469bc822393e58c9f0d174d57
method = rebase
cmdver = 0.4.1

View File

@@ -51,8 +51,6 @@
#include "offsets/1020_exfat.h"
#include "offsets/1100.h"
#include "offsets/1100_exfat.h"
#include "offsets/1200.h"
#include "offsets/1200_exfat.h"
#include "../utils/fatal.h"
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
@@ -115,8 +113,6 @@ DEFINE_OFFSET_STRUCT(_1020);
DEFINE_OFFSET_STRUCT(_1020_EXFAT);
DEFINE_OFFSET_STRUCT(_1100);
DEFINE_OFFSET_STRUCT(_1100_EXFAT);
DEFINE_OFFSET_STRUCT(_1200);
DEFINE_OFFSET_STRUCT(_1200_EXFAT);
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
switch (version) {
@@ -190,10 +186,6 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
return &(GET_OFFSET_STRUCT_NAME(_1100));
case FS_VER_11_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_1100_EXFAT));
case FS_VER_12_0_0:
return &(GET_OFFSET_STRUCT_NAME(_1200));
case FS_VER_12_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_1200_EXFAT));
default:
fatal_abort(Fatal_UnknownVersion);
}

View File

@@ -74,9 +74,6 @@ enum FS_VER
FS_VER_11_0_0,
FS_VER_11_0_0_EXFAT,
FS_VER_12_0_0,
FS_VER_12_0_0_EXFAT,
FS_VER_MAX,
};

View File

@@ -1,59 +0,0 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1200_H__
#define __FS_1200_H__
// Accessor vtable getters
#define FS_OFFSET_1200_SDMMC_ACCESSOR_GC 0x154FD0
#define FS_OFFSET_1200_SDMMC_ACCESSOR_SD 0x156DE0
#define FS_OFFSET_1200_SDMMC_ACCESSOR_NAND 0x155500
// Hooks
#define FS_OFFSET_1200_SDMMC_WRAPPER_READ 0x150970
#define FS_OFFSET_1200_SDMMC_WRAPPER_WRITE 0x150A30
#define FS_OFFSET_1200_RTLD 0x688
#define FS_OFFSET_1200_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
#define FS_OFFSET_1200_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0
// Misc funcs
#define FS_OFFSET_1200_LOCK_MUTEX 0x29350
#define FS_OFFSET_1200_UNLOCK_MUTEX 0x293A0
#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850
#define FS_OFFSET_1200_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0
// Misc Data
#define FS_OFFSET_1200_SD_MUTEX 0xE3D3E8
#define FS_OFFSET_1200_NAND_MUTEX 0xE38768
#define FS_OFFSET_1200_ACTIVE_PARTITION 0xE387A8
#define FS_OFFSET_1200_SDMMC_DAS_HANDLE 0xE20DB0
// NOPs
#define FS_OFFSET_1200_SD_DAS_INIT 0x27244
// Nintendo Paths
#define FS_OFFSET_1200_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1200_H__

View File

@@ -1,59 +0,0 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1200_EXFAT_H__
#define __FS_1200_EXFAT_H__
// Accessor vtable getters
#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_GC 0x154FD0
#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_SD 0x156DE0
#define FS_OFFSET_1200_EXFAT_SDMMC_ACCESSOR_NAND 0x155500
// Hooks
#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_READ 0x150970
#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_WRITE 0x150A30
#define FS_OFFSET_1200_EXFAT_RTLD 0x688
#define FS_OFFSET_1200_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
#define FS_OFFSET_1200_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x14FCC0
// Misc funcs
#define FS_OFFSET_1200_EXFAT_LOCK_MUTEX 0x29350
#define FS_OFFSET_1200_EXFAT_UNLOCK_MUTEX 0x293A0
#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150850
#define FS_OFFSET_1200_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1508E0
// Misc Data
#define FS_OFFSET_1200_EXFAT_SD_MUTEX 0xE4B3E8
#define FS_OFFSET_1200_EXFAT_NAND_MUTEX 0xE46768
#define FS_OFFSET_1200_EXFAT_ACTIVE_PARTITION 0xE467A8
#define FS_OFFSET_1200_EXFAT_SDMMC_DAS_HANDLE 0xE2EDB0
// NOPs
#define FS_OFFSET_1200_EXFAT_SD_DAS_INIT 0x27244
// Nintendo Paths
#define FS_OFFSET_1200_EXFAT_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x0006E810, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x0007AEC0, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x00081254, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x00092850, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1200_EXFAT_H__

View File

@@ -328,13 +328,13 @@ uint64_t sdmmc_wrapper_controller_open(int mmc_id)
if (_this != NULL)
{
// Lock eMMC xfer while SD card is being initialized by FS.
if (mmc_id == FS_SDMMC_SD)
if (_this == sdmmc_accessor_get(FS_SDMMC_SD))
mutex_lock_handler(FS_SDMMC_EMMC); // Recursive Mutex, handler will lock SD as well if custom_driver
result = _this->vtab->sdmmc_accessor_controller_open(_this);
// Unlock eMMC.
if (mmc_id == FS_SDMMC_SD)
if (_this == sdmmc_accessor_get(FS_SDMMC_SD))
mutex_unlock_handler(FS_SDMMC_EMMC);
return result;

View File

@@ -47,9 +47,9 @@ namespace ams::secmon::smc {
[fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IowaHynix1y4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_AulaHynix1y4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB,
[fuse::DramId_HoagHynix1y4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB,
[fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB,
@@ -286,10 +286,6 @@ namespace ams::secmon::smc {
/* Get the log configuration. */
args.r[1] = (static_cast<u64>(static_cast<u8>(secmon::GetLogPort())) << 32) | static_cast<u64>(secmon::GetLogBaudRate());
break;
case ConfigItem::ExosphereForceEnableUsb30:
/* Get whether usb 3.0 should be force-enabled. */
args.r[1] = GetSecmonConfiguration().IsUsb30ForceEnabled();
break;
default:
return SmcResult::InvalidArgument;
}

View File

@@ -50,7 +50,6 @@ namespace ams::secmon::smc {
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
};
SmcResult SmcGetConfigUser(SmcArguments &args);

View File

@@ -91,9 +91,6 @@ typedef enum {
FS_VER_11_0_0,
FS_VER_11_0_0_EXFAT,
FS_VER_12_0_0,
FS_VER_12_0_0_EXFAT,
FS_VER_MAX,
} emummc_fs_ver_t;

View File

@@ -33,7 +33,6 @@
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
#define EXOSPHERE_FLAG_FORCE_ENABLE_USB_30 (1 << 7u)
#define EXOSPHERE_LOG_FLAG_INVERTED (1 << 0u)

View File

@@ -426,9 +426,6 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
"\xE3\x99\x15\x6E\x84\x4E\xB0\xAA", /* FS_VER_11_0_0 */
"\x0B\xA1\x5B\xB3\x04\xB5\x05\x63", /* FS_VER_11_0_0_EXFAT */
"\xDC\x2A\x08\x49\x96\xBB\x3C\x01", /* FS_VER_12_0_0 */
"\xD5\xA5\xBF\x36\x64\x0C\x49\xEA", /* FS_VER_12_0_0_EXFAT */
};
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {

View File

@@ -568,7 +568,6 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv)[] = {0xA9B
*/
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_send)[] = {0xE0, 0x03, 0x15, 0xAA, 0xA8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x88, 0x4A, 0x3C, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9BF2FEA, 0xF94043EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002A8, 0xF9401D08, 0xAA1503E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/*
stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0xE0]
@@ -597,63 +596,6 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)[] = {0xA9B
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1100, proc_id_recv)[] = {0x08, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x18, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x7F, 0x40, 0xF9, 0x09, 0xFC, 0x60, 0xD3, 0xEA, 0x5B, 0x40, 0xF9};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400308, 0xF9401D08, 0xAA1803E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/*
stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0x98]
mov w10, #3
lsl x10, x10, #2
ldr x10, [x11, x10]
mov x9, #0x0000ffffffffffff
and x8, x10, x9
mov x9, #0xffff000000000000
and x10, x10, x9
mov x9, #0xfffe000000000000
cmp x10, x9
beq #0x20
stp x8, x9, [sp, #-0x10]!
ldr x8, [x22]
ldr x8, [x8, #0x38]
mov x0, x22
blr x8
ldp x8, x9, [sp],#0x10
mov x8, x0
ldp x10, x11, [sp],#0x10
mov x0, x8
*/
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send)[] = {0xE0, 0x03, 0x16, 0xAA, 0xC8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0xA8, 0x4A, 0x3B, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_send)[] = {0xA9BF2FEA, 0xF9404FEB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002C8, 0xF9401D08, 0xAA1603E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/*
stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0xE0]
mov w10, #3
lsl x10, x10, #2
ldr x10, [x11, x10]
mov x9, #0x0000ffffffffffff
and x8, x10, x9
mov x9, #0xffff000000000000
and x10, x10, x9
mov x9, #0xfffe000000000000
cmp x10, x9
beq #0x20
stp x8, x9, [sp, #-0x10]!
ldr x8, [x28]
ldr x8, [x8, #0x38]
mov x0, x28
blr x8
ldp x8, x9, [sp],#0x10
mov x8, x0
ldp x10, x11, [sp],#0x10
mov x0, x8
*/
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv)[] = {0x88, 0x03, 0x40, 0xF9, 0xE0, 0x03, 0x1C, 0xAA, 0x08, 0x1D, 0x40, 0xF9, 0x00, 0x01, 0x3F, 0xD6, 0x08, 0x4B, 0x3A, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv)[] = {0xA9BF2FEA, 0xF94073EB, 0x5280006A, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400388, 0xF9401D08, 0xAA1C03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/* svcControlCodeMemory Patches */
/* b.eq -> nop */
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
@@ -663,7 +605,6 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, system_memory_increase)[] = {0x52A3C008}; /* MOV W8, #0x1E000000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */
@@ -672,7 +613,6 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(800, system_memory_increase)[
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(1100, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase)[] = {0x52A3B015}; /* MOV W21, #0x1D800000 */
/* Hook Definitions. */
static const kernel_patch_t g_kernel_patches_100[] = {
@@ -995,35 +935,6 @@ static const kernel_patch_t g_kernel_patches_1101[] = {
}
};
static const kernel_patch_t g_kernel_patches_1200[] = {
{ /* Send Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_send),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_send))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_send)
},
{ /* Receive Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1200, proc_id_recv),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1200, proc_id_recv)
},
{ /* svcControlCodeMemory Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1200, svc_control_codememory),
.patch_offset = 0x2FCB4,
},
{ /* System Memory Increase Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1200, system_memory_increase),
.patch_offset = 0x4809C,
}
};
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
/* Kernel Infos. */
@@ -1127,15 +1038,6 @@ static const kernel_info_t g_kernel_infos[] = {
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x49EE8,
KERNEL_PATCHES(1101)
},
{ /* 12.0.0. */
.hash = {0x8D, 0x4A, 0x1E, 0xFC, 0xCC, 0x6C, 0xFE, 0x6C, 0x45, 0x14, 0x13, 0xA1, 0x7F, 0xF6, 0xDF, 0xFD, 0x7E, 0x5D, 0xD1, 0x38, 0xCE, 0x86, 0x11, 0x8B, 0x58, 0x5F, 0x89, 0x67, 0x84, 0x48, 0xA8, 0x17, },
.hash_offset = 0x1C4,
.hash_size = 0x68000 - 0x1C4,
.embedded_ini_offset = 0x68000,
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x48810,
KERNEL_PATCHES(1200)
}
};

View File

@@ -256,24 +256,6 @@ static int stratosphere_ini_handler(void *user, const char *section, const char
return 1;
}
static int system_settings_ini_handler(void *user, const char *section, const char *name, const char *value) {
uint32_t *flags = (uint32_t *)user;
if (strcmp(section, "usb") == 0) {
if (strcmp(name, "usb30_force_enabled") == 0) {
if (strcmp(value, "u8!0x1") == 0) {
*flags |= EXOSPHERE_FLAG_FORCE_ENABLE_USB_30;
} else if (strcmp(value, "u8!0x0") == 0) {
*flags &= ~(EXOSPHERE_FLAG_FORCE_ENABLE_USB_30);
}
} else {
return 0;
}
} else {
return 0;
}
return 1;
}
static bool is_nca_present(const char *nca_name) {
char path[0x100];
snprintf(path, sizeof(path), "system:/contents/registered/%s.nca", nca_name);
@@ -285,9 +267,7 @@ static bool is_nca_present(const char *nca_name) {
static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){
#define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0)
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_0) {
CHECK_NCA("bd4185843550fbba125b20787005d1d2", 12_0_0);
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) {
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) {
CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1);
CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0);
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
@@ -390,8 +370,6 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
return ATMOSPHERE_TARGET_FIRMWARE_10_0_0;
} else if (memcmp(package1loader_header->build_timestamp, "20201030", 8) == 0) {
return ATMOSPHERE_TARGET_FIRMWARE_11_0_0;
} else if (memcmp(package1loader_header->build_timestamp, "20210129", 8) == 0) {
return ATMOSPHERE_TARGET_FIRMWARE_12_0_0;
} else {
fatal_error("[NXBOOT] Unable to identify package1!\n");
}
@@ -559,15 +537,6 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
/* Apply lcd vendor. */
exo_cfg.lcd_vendor = display_get_lcd_vendor();
/* Read and parse system settings.ini to determine usb 3.0 enable. */
char *settings_ini = calloc(1, 0x20000);
if (read_from_file(settings_ini, 0x1FFFF, "atmosphere/config/system_settings.ini")) {
if (ini_parse_string(settings_ini, system_settings_ini_handler, &exo_cfg.flags[0]) < 0) {
fatal_error("[NXBOOT] Failed to parse system_settings.ini!\n");
}
}
free(settings_ini);
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n");
}
@@ -599,10 +568,6 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) {
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0 && !(fuse_get_reserved_odm(7) & ~0x00001FFF)) {
kip_patches_set_enable_nogc();
}
/* NOTE: 12.0.0 added a new lotus firmware, but did not burn a fuse. */
/* This is literally undetectable using normal fuses.... */
/* C'est la vie. */
}
}

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 86c2eec8e9e966a30c19692adb79faeda45c1940
parent = aa2d03d8e13bc5d3f34751b6105503a601dc958e
commit = 6c11c07e2a7f03952a4e70eb89b47bf528de39c6
parent = 9e104bb83f1302e9f126542fbf57c7f666aae953
method = merge
cmdver = 0.4.1

View File

@@ -51,9 +51,9 @@ namespace ams::fuse {
DramId_IcosaSamsung4GB = 0,
DramId_IcosaHynix4GB = 1,
DramId_IcosaMicron4GB = 2,
DramId_IowaHynix1y4GB = 3,
DramId_AulaHynix1y4GB = 3,
DramId_IcosaSamsung6GB = 4,
DramId_HoagHynix1y4GB = 5,
DramId_CopperHynix4GB = 5,
DramId_CopperMicron4GB = 6,
DramId_IowaX1X2Samsung4GB = 7,
DramId_IowaSansung4GB = 8,

View File

@@ -29,7 +29,6 @@ namespace ams::secmon {
SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4),
SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5),
SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6),
SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7),
SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel,
};
@@ -102,7 +101,6 @@ namespace ams::secmon {
constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags[0] & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; }
constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; }
constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags[0] & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; }
constexpr bool IsUsb30ForceEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ForceEnableUsb30) != 0; }
constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); }
};

View File

@@ -38,67 +38,37 @@ namespace ams::kern::arch::arm64::init {
public:
class IPageAllocator {
public:
virtual KPhysicalAddress Allocate(size_t size) = 0;
virtual void Free(KPhysicalAddress phys_addr, size_t size) = 0;
virtual KPhysicalAddress Allocate() { return Null<KPhysicalAddress>; }
virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); }
};
private:
KPhysicalAddress m_l1_tables[2];
u32 m_num_entries[2];
struct NoClear{};
private:
KPhysicalAddress m_l1_table;
public:
KInitialPageTable(KVirtualAddress start_address, KVirtualAddress end_address, IPageAllocator &allocator) {
/* Set tables. */
m_l1_tables[0] = AllocateNewPageTable(allocator);
m_l1_tables[1] = AllocateNewPageTable(allocator);
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1, NoClear) : m_l1_table(l1) { /* ... */ }
/* Set counts. */
m_num_entries[0] = MaxPageTableEntries;
m_num_entries[1] = ((end_address / L1BlockSize) & (MaxPageTableEntries - 1)) - ((start_address / L1BlockSize) & (MaxPageTableEntries - 1)) + 1;
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : KInitialPageTable(l1, NoClear{}) {
ClearNewPageTable(m_l1_table);
}
KInitialPageTable() {
/* Set tables. */
m_l1_tables[0] = util::AlignDown(cpu::GetTtbr0El1(), PageSize);
m_l1_tables[1] = util::AlignDown(cpu::GetTtbr1El1(), PageSize);
/* Set counts. */
cpu::TranslationControlRegisterAccessor tcr;
m_num_entries[0] = tcr.GetT0Size() / L1BlockSize;
m_num_entries[1] = tcr.GetT1Size() / L1BlockSize;
/* Check counts. */
MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[0] && m_num_entries[0] <= MaxPageTableEntries);
MESOSPHERE_INIT_ABORT_UNLESS(0 < m_num_entries[1] && m_num_entries[1] <= MaxPageTableEntries);
}
constexpr ALWAYS_INLINE uintptr_t GetTtbr0L1TableAddress() const {
return GetInteger(m_l1_tables[0]);
}
constexpr ALWAYS_INLINE uintptr_t GetTtbr1L1TableAddress() const {
return GetInteger(m_l1_tables[1]);
constexpr ALWAYS_INLINE uintptr_t GetL1TableAddress() const {
return GetInteger(m_l1_table);
}
private:
constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KVirtualAddress address) const {
const size_t index = (GetInteger(address) >> (BITSIZEOF(address) - 1)) & 1;
L1PageTableEntry *l1_table = reinterpret_cast<L1PageTableEntry *>(GetInteger(m_l1_tables[index]));
return l1_table + ((GetInteger(address) / L1BlockSize) & (m_num_entries[index] - 1));
static constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KPhysicalAddress _l1_table, KVirtualAddress address) {
L1PageTableEntry *l1_table = reinterpret_cast<L1PageTableEntry *>(GetInteger(_l1_table));
return l1_table + ((GetInteger(address) >> 30) & (MaxPageTableEntries - 1));
}
static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address) {
L2PageTableEntry *l2_table = reinterpret_cast<L2PageTableEntry *>(GetInteger(entry->GetTable()));
return l2_table + ((GetInteger(address) / L2BlockSize) & (MaxPageTableEntries - 1));
return l2_table + ((GetInteger(address) >> 21) & (MaxPageTableEntries - 1));
}
static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address) {
L3PageTableEntry *l3_table = reinterpret_cast<L3PageTableEntry *>(GetInteger(entry->GetTable()));
return l3_table + ((GetInteger(address) / L3BlockSize) & (MaxPageTableEntries - 1));
}
static ALWAYS_INLINE KPhysicalAddress AllocateNewPageTable(IPageAllocator &allocator) {
auto address = allocator.Allocate(PageSize);
ClearNewPageTable(address);
return address;
return l3_table + ((GetInteger(address) >> 12) & (MaxPageTableEntries - 1));
}
static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) {
@@ -113,7 +83,7 @@ namespace ams::kern::arch::arm64::init {
const KVirtualAddress end_virt_addr = virt_addr + size;
size_t count = 0;
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* If an L1 block is mapped or we're empty, advance by L1BlockSize. */
if (l1_entry->IsBlock() || l1_entry->IsEmpty()) {
@@ -167,7 +137,7 @@ namespace ams::kern::arch::arm64::init {
const KVirtualAddress end_virt_addr = virt_addr + size;
size_t count = 0;
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* If an L1 block is mapped or we're empty, advance by L1BlockSize. */
if (l1_entry->IsBlock() || l1_entry->IsEmpty()) {
@@ -224,7 +194,7 @@ namespace ams::kern::arch::arm64::init {
}
PageTableEntry *GetMappingEntry(KVirtualAddress virt_addr, size_t block_size) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
if (l1_entry->IsBlock()) {
MESOSPHERE_INIT_ABORT_UNLESS(block_size == L1BlockSize);
@@ -331,7 +301,7 @@ namespace ams::kern::arch::arm64::init {
/* Iteratively map pages until the requested region is mapped. */
while (size > 0) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* Can we make an L1 block? */
if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && size >= L1BlockSize) {
@@ -346,7 +316,7 @@ namespace ams::kern::arch::arm64::init {
/* If we don't already have an L2 table, we need to make a new one. */
if (!l1_entry->IsTable()) {
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
KPhysicalAddress new_table = allocator.Allocate();
ClearNewPageTable(new_table);
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
cpu::DataSynchronizationBarrierInnerShareable();
@@ -380,7 +350,7 @@ namespace ams::kern::arch::arm64::init {
/* If we don't already have an L3 table, we need to make a new one. */
if (!l2_entry->IsTable()) {
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
KPhysicalAddress new_table = allocator.Allocate();
ClearNewPageTable(new_table);
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
cpu::DataSynchronizationBarrierInnerShareable();
@@ -412,7 +382,7 @@ namespace ams::kern::arch::arm64::init {
KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const {
/* Get the L1 entry. */
const L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
const L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
if (l1_entry->IsBlock()) {
return l1_entry->GetBlock() + (GetInteger(virt_addr) & (L1BlockSize - 1));
@@ -474,7 +444,7 @@ namespace ams::kern::arch::arm64::init {
};
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* If an L1 block is mapped, update. */
if (l1_entry->IsBlock()) {
@@ -515,7 +485,7 @@ namespace ams::kern::arch::arm64::init {
const KVirtualAddress end_virt_addr = virt_addr + size;
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* If an L1 block is mapped, the address isn't free. */
if (l1_entry->IsBlock()) {
@@ -564,7 +534,7 @@ namespace ams::kern::arch::arm64::init {
/* Iteratively reprotect pages until the requested region is reprotected. */
while (size > 0) {
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
L1PageTableEntry *l1_entry = GetL1Entry(m_l1_table, virt_addr);
/* Check if an L1 block is present. */
if (l1_entry->IsBlock()) {
@@ -703,18 +673,11 @@ namespace ams::kern::arch::arm64::init {
};
class KInitialPageAllocator final : public KInitialPageTable::IPageAllocator {
private:
static constexpr inline size_t FreeUnitSize = BITSIZEOF(u64) * PageSize;
struct FreeListEntry {
FreeListEntry *next;
size_t size;
};
class KInitialPageAllocator : public KInitialPageTable::IPageAllocator {
public:
struct State {
uintptr_t start_address;
uintptr_t end_address;
FreeListEntry *free_head;
uintptr_t next_address;
uintptr_t free_bitmap;
};
private:
State m_state;
@@ -722,8 +685,8 @@ namespace ams::kern::arch::arm64::init {
constexpr ALWAYS_INLINE KInitialPageAllocator() : m_state{} { /* ... */ }
ALWAYS_INLINE void Initialize(uintptr_t address) {
m_state.start_address = address;
m_state.end_address = address;
m_state.next_address = address + BITSIZEOF(m_state.free_bitmap) * PageSize;
m_state.free_bitmap = ~uintptr_t();
}
ALWAYS_INLINE void InitializeFromState(uintptr_t state_val) {
@@ -734,134 +697,28 @@ namespace ams::kern::arch::arm64::init {
*out = m_state;
m_state = {};
}
private:
bool CanAllocate(size_t align, size_t size) const {
for (auto *cur = m_state.free_head; cur != nullptr; cur = cur->next) {
const uintptr_t cur_last = reinterpret_cast<uintptr_t>(cur) + cur->size - 1;
const uintptr_t alloc_last = util::AlignUp(reinterpret_cast<uintptr_t>(cur), align) + size - 1;
if (alloc_last <= cur_last) {
return true;
}
}
return false;
}
bool TryAllocate(uintptr_t address, size_t size) {
/* Try to allocate the region. */
auto **prev_next = std::addressof(m_state.free_head);
for (auto *cur = m_state.free_head; cur != nullptr; prev_next = std::addressof(cur->next), cur = cur->next) {
const uintptr_t cur_start = reinterpret_cast<uintptr_t>(cur);
const uintptr_t cur_last = cur_start + cur->size - 1;
if (cur_start <= address && address + size - 1 <= cur_last) {
auto *alloc = reinterpret_cast<FreeListEntry *>(address);
/* Perform fragmentation at front. */
if (cur != alloc) {
prev_next = std::addressof(cur->next);
*alloc = {
.next = cur->next,
.size = cur_start + cur->size - address,
};
*cur = {
.next = alloc,
.size = address - cur_start,
};
}
/* Perform fragmentation at tail. */
if (alloc->size != size) {
auto *next = reinterpret_cast<FreeListEntry *>(address + size);
*next = {
.next = alloc->next,
.size = alloc->size - size,
};
*alloc = {
.next = next,
.size = size,
};
}
*prev_next = alloc->next;
return true;
}
}
return false;
}
public:
KPhysicalAddress Allocate(size_t align, size_t size) {
/* Ensure that the free list is non-empty. */
while (!this->CanAllocate(align, size)) {
this->Free(m_state.end_address, FreeUnitSize);
m_state.end_address += FreeUnitSize;
}
/* Allocate a random address. */
const uintptr_t aligned_start = util::AlignUp(m_state.start_address, align);
const uintptr_t aligned_end = util::AlignDown(m_state.end_address, align);
const size_t ind_max = ((aligned_end - aligned_start) / align) - 1;
while (true) {
if (const uintptr_t random_address = aligned_start + (KSystemControl::Init::GenerateRandomRange(0, ind_max) * align); this->TryAllocate(random_address, size)) {
return random_address;
}
}
}
virtual KPhysicalAddress Allocate(size_t size) override {
return this->Allocate(size, size);
}
virtual void Free(KPhysicalAddress phys_addr, size_t size) override {
auto **prev_next = std::addressof(m_state.free_head);
auto *new_chunk = reinterpret_cast<FreeListEntry *>(GetInteger(phys_addr));
if (auto *cur = m_state.free_head; cur != nullptr) {
const uintptr_t new_start = reinterpret_cast<uintptr_t>(new_chunk);
const uintptr_t new_end = GetInteger(phys_addr) + size;
while (true) {
/* Attempt coalescing. */
const uintptr_t cur_start = reinterpret_cast<uintptr_t>(cur);
const uintptr_t cur_end = cur_start + cur->size;
if (new_start < new_end) {
if (new_end < cur_start) {
*new_chunk = {
.next = cur,
.size = size,
};
break;
} else if (new_end == cur_start) {
*new_chunk = {
.next = cur->next,
.size = cur->size + size,
};
break;
}
} else if (cur_end == new_start) {
cur->size += size;
return;
}
prev_next = std::addressof(cur->next);
if (cur->next != nullptr) {
cur = cur->next;
} else {
*new_chunk = {
.next = nullptr,
.size = size,
};
cur->next = new_chunk;
return;
}
}
virtual KPhysicalAddress Allocate() override {
MESOSPHERE_INIT_ABORT_UNLESS(m_state.next_address != Null<uintptr_t>);
uintptr_t allocated = m_state.next_address;
if (m_state.free_bitmap != 0) {
u64 index;
uintptr_t mask;
do {
index = KSystemControl::Init::GenerateRandomRange(0, BITSIZEOF(m_state.free_bitmap) - 1);
mask = (static_cast<uintptr_t>(1) << index);
} while ((m_state.free_bitmap & mask) == 0);
m_state.free_bitmap &= ~mask;
allocated = m_state.next_address - ((BITSIZEOF(m_state.free_bitmap) - index) * PageSize);
} else {
*new_chunk = {
.next = nullptr,
.size = size,
};
m_state.next_address += PageSize;
}
*prev_next = new_chunk;
ClearPhysicalMemory(allocated, PageSize);
return allocated;
}
/* No need to override free. The default does nothing, and so would we. */
};
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define THREAD_STACK_PARAMETERS_SIZE 0x30
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
#define THREAD_STACK_PARAMETERS_CONTEXT 0x18
#define THREAD_STACK_PARAMETERS_CUR_THREAD 0x20
#define THREAD_STACK_PARAMETERS_DISABLE_COUNT 0x28
#define THREAD_STACK_PARAMETERS_DPC_FLAGS 0x2A
#define THREAD_STACK_PARAMETERS_CURRENT_SVC_ID 0x2B
#define THREAD_STACK_PARAMETERS_IS_CALLING_SVC 0x2C
#define THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER 0x2D
#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E

View File

@@ -232,7 +232,7 @@ namespace ams::kern::arch::arm64::cpu {
}
ALWAYS_INLINE void SetExceptionThreadStackTop(uintptr_t top) {
cpu::SetCntvCvalEl0(top);
SetTpidrEl1(top);
}
ALWAYS_INLINE void SwitchThreadLocalRegion(uintptr_t tlr) {

View File

@@ -74,7 +74,6 @@ namespace ams::kern::arch::arm64::cpu {
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntkCtlEl1, cntkctl_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCtlEl0, cntp_ctl_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCvalEl0, cntp_cval_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntvCvalEl0, cntv_cval_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Daif, daif)
@@ -198,11 +197,6 @@ namespace ams::kern::arch::arm64::cpu {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(TranslationControl, tcr_el1)
constexpr ALWAYS_INLINE size_t GetT0Size() const {
const size_t shift_value = this->GetBits(0, 6);
return size_t(1) << (size_t(64) - shift_value);
}
constexpr ALWAYS_INLINE size_t GetT1Size() const {
const size_t shift_value = this->GetBits(16, 6);
return size_t(1) << (size_t(64) - shift_value);

View File

@@ -85,6 +85,7 @@ namespace ams::kern::arch::arm64 {
NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level);
NOINLINE Result UnbindHandler(s32 irq, s32 core);
NOINLINE Result ClearInterrupt(s32 irq);
NOINLINE Result ClearInterrupt(s32 irq, s32 core_id);
ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) {

View File

@@ -176,7 +176,7 @@ namespace ams::kern::arch::arm64 {
}
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit);
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager);
Result Finalize();
private:
Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll);

View File

@@ -30,16 +30,12 @@ namespace ams::kern::arch::arm64 {
m_page_table.Activate(id);
}
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) {
return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager, resource_limit);
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
return m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager);
}
void Finalize() { m_page_table.Finalize(); }
ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() {
return m_page_table.AcquireDeviceMapLock();
}
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) {
return m_page_table.SetMemoryPermission(addr, size, perm);
}
@@ -140,42 +136,26 @@ namespace ams::kern::arch::arm64 {
return m_page_table.ReadDebugMemory(buffer, address, size);
}
Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) {
return m_page_table.ReadDebugIoMemory(buffer, address, size);
}
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size) {
return m_page_table.WriteDebugMemory(address, buffer, size);
}
Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) {
return m_page_table.WriteDebugIoMemory(address, buffer, size);
}
Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
return m_page_table.LockForMapDeviceAddressSpace(address, size, perm, is_aligned);
}
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) {
return m_page_table.LockForUnmapDeviceAddressSpace(address, size);
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
return m_page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned);
}
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
return m_page_table.UnlockForDeviceAddressSpace(address, size);
}
Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size) {
return m_page_table.MakePageGroupForUnmapDeviceAddressSpace(out, address, size);
}
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size) {
return m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size, mapped_size);
}
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
return m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm, is_aligned);
}
Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) {
return m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size);
}
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) {
return m_page_table.LockForIpcUserBuffer(out, address, size);
}
@@ -200,10 +180,6 @@ namespace ams::kern::arch::arm64 {
return m_page_table.UnlockForCodeMemory(address, size, pg);
}
Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size) {
return m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size);
}
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) {
return m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr);
}
@@ -232,8 +208,8 @@ namespace ams::kern::arch::arm64 {
return m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, test_perm, dst_state, send);
}
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) {
return m_page_table.CleanupForIpcServer(address, size, dst_state);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) {
return m_page_table.CleanupForIpcServer(address, size, dst_state, server_process);
}
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
@@ -256,10 +232,6 @@ namespace ams::kern::arch::arm64 {
return m_page_table.UnmapPhysicalMemoryUnsafe(address, size);
}
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KProcessPageTable &src_page_table, KProcessAddress src_address) {
return m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table, src_address);
}
void DumpMemoryBlocks() const {
return m_page_table.DumpMemoryBlocks();
}
@@ -318,10 +290,6 @@ namespace ams::kern::arch::arm64 {
KBlockInfoManager *GetBlockInfoManager() {
return m_page_table.GetBlockInfoManager();
}
KPageTableBase &GetBasePageTable() {
return m_page_table;
}
};
}

View File

@@ -69,8 +69,8 @@ namespace ams::kern::board::nintendo::nx {
Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size);
Result Detach(ams::svc::DeviceName device_name);
Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address);
Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address);
void Unmap(KDeviceVirtualAddress device_address, size_t size) {
return this->UnmapImpl(device_address, size, false);
@@ -78,11 +78,12 @@ namespace ams::kern::board::nintendo::nx {
private:
Result MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm);
Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned);
Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm);
void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force);
bool IsFree(KDeviceVirtualAddress address, u64 size) const;
bool Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const;
Result MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const;
bool Compare(const KPageGroup &pg, KDeviceVirtualAddress device_address) const;
public:
static void Initialize();

View File

@@ -25,7 +25,6 @@ namespace ams::kern::board::nintendo::nx {
/* Initialization. */
static size_t GetIntendedMemorySize();
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static KPhysicalAddress GetInitialProcessBinaryPhysicalAddress();
static bool ShouldIncreaseThreadResourceLimit();
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
static size_t GetApplicationPoolSize();

View File

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

View File

@@ -17,7 +17,6 @@
#include <vapours.hpp>
#include <mesosphere/kern_build_config.hpp>
#include <mesosphere/svc/kern_svc_results.hpp>
#include <mesosphere/kern_select_assembly_offsets.h>
namespace ams::kern {

View File

@@ -29,12 +29,11 @@ namespace ams::kern {
u32 reserved;
};
NOINLINE size_t CopyInitialProcessBinaryToKernelMemory();
NOINLINE void CopyInitialProcessBinaryToKernelMemory();
NOINLINE void CreateAndRunInitialProcesses();
u64 GetInitialProcessIdMin();
u64 GetInitialProcessIdMax();
KVirtualAddress GetInitialProcessBinaryAddress();
size_t GetInitialProcessesSecureMemorySize();
}

View File

@@ -69,12 +69,11 @@ namespace ams::kern {
private:
MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
private:
KAutoObject *m_next_closed_object;
std::atomic<u32> m_ref_count;
public:
static KAutoObject *Create(KAutoObject *ptr);
public:
constexpr ALWAYS_INLINE explicit KAutoObject() : m_next_closed_object(nullptr), m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); }
constexpr ALWAYS_INLINE explicit KAutoObject() : m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); }
virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); }
/* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */
@@ -121,7 +120,7 @@ namespace ams::kern {
}
}
NOINLINE bool Open() {
ALWAYS_INLINE bool Open() {
MESOSPHERE_ASSERT_THIS();
/* Atomically increment the reference count, only if it's positive. */
@@ -137,7 +136,7 @@ namespace ams::kern {
return true;
}
NOINLINE void Close() {
ALWAYS_INLINE void Close() {
MESOSPHERE_ASSERT_THIS();
/* Atomically decrement the reference count, not allowing it to become negative. */
@@ -146,19 +145,11 @@ namespace ams::kern {
MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed));
/* If ref count hits zero, schedule the object for destruction. */
/* If ref count hits zero, destroy the object. */
if (cur_ref_count - 1 == 0) {
this->ScheduleDestruction();
this->Destroy();
}
}
private:
/* NOTE: This has to be defined *after* KThread is defined. */
/* Nintendo seems to handle this by defining Open/Close() in a cpp, but we'd like them to remain in headers. */
/* Implementation for this will be inside kern_k_thread.hpp, so it can be ALWAYS_INLINE. */
void ScheduleDestruction();
public:
/* Getter, for KThread. */
ALWAYS_INLINE KAutoObject *GetNextClosedObject() { return m_next_closed_object; }
};
class KAutoObjectWithListContainer;
@@ -207,7 +198,7 @@ namespace ams::kern {
}
}
ALWAYS_INLINE ~KScopedAutoObject() {
~KScopedAutoObject() {
if (m_obj != nullptr) {
m_obj->Close();
}

View File

@@ -47,7 +47,6 @@ namespace ams::kern {
ALWAYS_INLINE s32 GetMaxSessions() const { return m_max_sessions; }
bool IsLight() const;
bool IsServerClosed() const;
/* Overridden virtual functions. */
virtual void Destroy() override;

View File

@@ -23,7 +23,7 @@ namespace ams::kern {
class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
private:
util::TypedStorage<KPageGroup> m_page_group;
TYPED_STORAGE(KPageGroup) m_page_group;
KProcess *m_owner;
KProcessAddress m_address;
KLightLock m_lock;

View File

@@ -36,7 +36,7 @@ namespace ams::kern {
void Signal(uintptr_t cv_key, s32 count);
Result Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout);
private:
void SignalImpl(KThread *thread);
KThread *SignalImpl(KThread *thread);
};
ALWAYS_INLINE void BeforeUpdatePriority(KConditionVariable::ThreadTree *tree, KThread *thread) {

View File

@@ -113,7 +113,7 @@ namespace ams::kern {
if (AMS_LIKELY(allocated != nullptr)) {
/* Construct the object. */
std::construct_at(allocated);
new (allocated) T();
/* Update our tracking. */
size_t used = m_used.fetch_add(1) + 1;

View File

@@ -53,29 +53,46 @@ namespace ams::kern {
return pack.Get<HandleEncoded>();
}
union EntryInfo {
struct {
u16 linear_id;
u16 type;
} info;
s32 next_free_index;
class Entry {
private:
union {
struct {
u16 linear_id;
u16 type;
} info;
Entry *next_free_entry;
} m_meta;
KAutoObject *m_object;
public:
constexpr Entry() : m_meta(), m_object(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE u16 GetLinearId() const { return info.linear_id; }
constexpr ALWAYS_INLINE u16 GetType() const { return info.type; }
constexpr ALWAYS_INLINE s32 GetNextFreeIndex() const { return next_free_index; }
constexpr ALWAYS_INLINE void SetFree(Entry *next) {
m_object = nullptr;
m_meta.next_free_entry = next;
}
constexpr ALWAYS_INLINE void SetUsed(KAutoObject *obj, u16 linear_id, u16 type) {
m_object = obj;
m_meta.info = { linear_id, type };
}
constexpr ALWAYS_INLINE KAutoObject *GetObject() const { return m_object; }
constexpr ALWAYS_INLINE Entry *GetNextFreeEntry() const { return m_meta.next_free_entry; }
constexpr ALWAYS_INLINE u16 GetLinearId() const { return m_meta.info.linear_id; }
constexpr ALWAYS_INLINE u16 GetType() const { return m_meta.info.type; }
};
private:
EntryInfo m_entry_infos[MaxTableSize];
KAutoObject *m_objects[MaxTableSize];
s32 m_free_head_index;
mutable KSpinLock m_lock;
Entry *m_table;
Entry *m_free_head;
Entry m_entries[MaxTableSize];
u16 m_table_size;
u16 m_max_count;
u16 m_next_linear_id;
u16 m_count;
mutable KSpinLock m_lock;
public:
constexpr KHandleTable() :
m_entry_infos(), m_objects(), m_free_head_index(-1), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0), m_lock()
m_lock(), m_table(nullptr), m_free_head(nullptr), m_entries(), m_table_size(0), m_max_count(0), m_next_linear_id(MinLinearId), m_count(0)
{ MESOSPHERE_ASSERT_THIS(); }
constexpr NOINLINE Result Initialize(s32 size) {
@@ -84,18 +101,19 @@ namespace ams::kern {
R_UNLESS(size <= static_cast<s32>(MaxTableSize), svc::ResultOutOfMemory());
/* Initialize all fields. */
m_max_count = 0;
m_table_size = (size <= 0) ? MaxTableSize : size;
m_next_linear_id = MinLinearId;
m_count = 0;
m_free_head_index = -1;
m_table = m_entries;
m_table_size = (size <= 0) ? MaxTableSize : size;
m_next_linear_id = MinLinearId;
m_count = 0;
m_max_count = 0;
/* Free all entries. */
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
for (size_t i = 0; i < static_cast<size_t>(m_table_size - 1); i++) {
m_entries[i].SetFree(std::addressof(m_entries[i + 1]));
}
m_entries[m_table_size - 1].SetFree(nullptr);
m_free_head = std::addressof(m_entries[0]);
return ResultSuccess();
}
@@ -116,7 +134,7 @@ namespace ams::kern {
if constexpr (std::is_same<T, KAutoObject>::value) {
return this->GetObjectImpl(handle);
} else {
if (auto *obj = this->GetObjectImpl(handle); AMS_LIKELY(obj != nullptr)) {
if (auto *obj = this->GetObjectImpl(handle); obj != nullptr) {
return obj->DynamicCast<T*>();
} else {
return nullptr;
@@ -238,29 +256,27 @@ namespace ams::kern {
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type);
constexpr ALWAYS_INLINE s32 AllocateEntry() {
constexpr ALWAYS_INLINE Entry *AllocateEntry() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(m_count < m_table_size);
const auto index = m_free_head_index;
Entry *entry = m_free_head;
m_free_head = entry->GetNextFreeEntry();
m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
m_count++;
m_max_count = std::max(m_max_count, m_count);
m_max_count = std::max(m_max_count, ++m_count);
return index;
return entry;
}
constexpr ALWAYS_INLINE void FreeEntry(s32 index) {
constexpr ALWAYS_INLINE void FreeEntry(Entry *entry) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(m_count > 0);
m_objects[index] = nullptr;
m_entry_infos[index].next_free_index = m_free_head_index;
entry->SetFree(m_free_head);
m_free_head = entry;
m_free_head_index = index;
--m_count;
m_count--;
}
constexpr ALWAYS_INLINE u16 AllocateLinearId() {
@@ -271,7 +287,13 @@ namespace ams::kern {
return id;
}
constexpr ALWAYS_INLINE bool IsValidHandle(ams::svc::Handle handle) const {
constexpr ALWAYS_INLINE size_t GetEntryIndex(Entry *entry) {
const size_t index = entry - m_table;
MESOSPHERE_ASSERT(index < m_table_size);
return index;
}
constexpr ALWAYS_INLINE Entry *FindEntry(ams::svc::Handle handle) const {
MESOSPHERE_ASSERT_THIS();
/* Unpack the handle. */
@@ -284,38 +306,38 @@ namespace ams::kern {
MESOSPHERE_UNUSED(reserved);
/* Validate our indexing information. */
if (AMS_UNLIKELY(raw_value == 0)) {
return false;
if (raw_value == 0) {
return nullptr;
}
if (AMS_UNLIKELY(linear_id == 0)) {
return false;
if (linear_id == 0) {
return nullptr;
}
if (AMS_UNLIKELY(index >= m_table_size)) {
return false;
if (index >= m_table_size) {
return nullptr;
}
/* Check that there's an object, and our serial id is correct. */
if (AMS_UNLIKELY(m_objects[index] == nullptr)) {
return false;
/* Get the entry, and ensure our serial id is correct. */
Entry *entry = std::addressof(m_table[index]);
if (entry->GetObject() == nullptr) {
return nullptr;
}
if (AMS_UNLIKELY(m_entry_infos[index].GetLinearId() != linear_id)) {
return false;
if (entry->GetLinearId() != linear_id) {
return nullptr;
}
return true;
return entry;
}
constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
MESOSPHERE_ASSERT_THIS();
/* Handles must not have reserved bits set. */
const auto handle_pack = GetHandleBitPack(handle);
if (AMS_UNLIKELY(handle_pack.Get<HandleReserved>() != 0)) {
if (GetHandleBitPack(handle).Get<HandleReserved>() != 0) {
return nullptr;
}
if (AMS_LIKELY(this->IsValidHandle(handle))) {
return m_objects[handle_pack.Get<HandleIndex>()];
if (Entry *entry = this->FindEntry(handle); entry != nullptr) {
return entry->GetObject();
} else {
return nullptr;
}
@@ -325,17 +347,18 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS();
/* Index must be in bounds. */
if (AMS_UNLIKELY(index >= m_table_size)) {
if (index >= m_table_size || m_table == nullptr) {
return nullptr;
}
/* Ensure entry has an object. */
if (KAutoObject *obj = m_objects[index]; obj != nullptr) {
*out_handle = EncodeHandle(index, m_entry_infos[index].GetLinearId());
return obj;
} else {
Entry *entry = std::addressof(m_table[index]);
if (entry->GetObject() == nullptr) {
return nullptr;
}
*out_handle = EncodeHandle(index, entry->GetLinearId());
return entry->GetObject();
}
};

View File

@@ -70,7 +70,6 @@ namespace ams::kern {
constexpr bool Is64Bit() const { return (m_flags & (1 << 3)); }
constexpr bool Is64BitAddressSpace() const { return (m_flags & (1 << 4)); }
constexpr bool UsesSecureMemory() const { return (m_flags & (1 << 5)); }
constexpr bool IsImmortal() const { return (m_flags & (1 << 6)); }
constexpr u32 GetRxAddress() const { return m_rx_address; }
constexpr u32 GetRxSize() const { return m_rx_size; }
@@ -91,49 +90,45 @@ namespace ams::kern {
class KInitialProcessReader {
private:
KInitialProcessHeader m_kip_header;
KInitialProcessHeader *m_kip_header;
public:
constexpr KInitialProcessReader() : m_kip_header() { /* ... */ }
constexpr const u32 *GetCapabilities() const { return m_kip_header.GetCapabilities(); }
constexpr size_t GetNumCapabilities() const { return m_kip_header.GetNumCapabilities(); }
constexpr const u32 *GetCapabilities() const { return m_kip_header->GetCapabilities(); }
constexpr size_t GetNumCapabilities() const { return m_kip_header->GetNumCapabilities(); }
constexpr size_t GetBinarySize() const {
return m_kip_header.GetRxCompressedSize() + m_kip_header.GetRoCompressedSize() + m_kip_header.GetRwCompressedSize();
return sizeof(*m_kip_header) + m_kip_header->GetRxCompressedSize() + m_kip_header->GetRoCompressedSize() + m_kip_header->GetRwCompressedSize();
}
constexpr size_t GetSize() const {
if (const size_t bss_size = m_kip_header.GetBssSize(); bss_size != 0) {
return util::AlignUp(m_kip_header.GetBssAddress() + m_kip_header.GetBssSize(), PageSize);
if (const size_t bss_size = m_kip_header->GetBssSize(); bss_size != 0) {
return m_kip_header->GetBssAddress() + m_kip_header->GetBssSize();
} else {
return util::AlignUp(m_kip_header.GetRwAddress() + m_kip_header.GetRwSize(), PageSize);
return m_kip_header->GetRwAddress() + m_kip_header->GetRwSize();
}
}
constexpr u8 GetPriority() const { return m_kip_header.GetPriority(); }
constexpr u8 GetIdealCoreId() const { return m_kip_header.GetIdealCoreId(); }
constexpr u32 GetAffinityMask() const { return m_kip_header.GetAffinityMask(); }
constexpr u32 GetStackSize() const { return m_kip_header.GetStackSize(); }
constexpr u8 GetPriority() const { return m_kip_header->GetPriority(); }
constexpr u8 GetIdealCoreId() const { return m_kip_header->GetIdealCoreId(); }
constexpr u32 GetAffinityMask() const { return m_kip_header->GetAffinityMask(); }
constexpr u32 GetStackSize() const { return m_kip_header->GetStackSize(); }
constexpr bool Is64Bit() const { return m_kip_header.Is64Bit(); }
constexpr bool Is64BitAddressSpace() const { return m_kip_header.Is64BitAddressSpace(); }
constexpr bool UsesSecureMemory() const { return m_kip_header.UsesSecureMemory(); }
constexpr bool IsImmortal() const { return m_kip_header.IsImmortal(); }
constexpr bool Is64Bit() const { return m_kip_header->Is64Bit(); }
constexpr bool Is64BitAddressSpace() const { return m_kip_header->Is64BitAddressSpace(); }
constexpr bool UsesSecureMemory() const { return m_kip_header->UsesSecureMemory(); }
KVirtualAddress Attach(KVirtualAddress bin) {
/* Copy the header. */
m_kip_header = *GetPointer<const KInitialProcessHeader>(bin);
/* Check that it's valid. */
if (m_kip_header.IsValid()) {
return bin + sizeof(KInitialProcessHeader);
bool Attach(u8 *bin) {
if (KInitialProcessHeader *header = reinterpret_cast<KInitialProcessHeader *>(bin); header->IsValid()) {
m_kip_header = header;
return true;
} else {
return Null<KVirtualAddress>;
return false;
}
}
Result MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const;
Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter &params, KProcessAddress src) const;
Result Load(KProcessAddress address, const ams::svc::CreateProcessParameter &params) const;
Result SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter &params) const;
};

View File

@@ -28,10 +28,9 @@ namespace ams::kern {
MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent);
private:
s32 m_interrupt_id;
s32 m_core_id;
bool m_is_initialized;
public:
constexpr KInterruptEvent() : m_interrupt_id(-1), m_core_id(-1), m_is_initialized(false) { /* ... */ }
constexpr KInterruptEvent() : m_interrupt_id(-1), m_is_initialized(false) { /* ... */ }
virtual ~KInterruptEvent() { /* ... */ }
Result Initialize(int32_t interrupt_name, ams::svc::InterruptType type);
@@ -59,9 +58,9 @@ namespace ams::kern {
virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override;
virtual void DoTask() override;
void Unregister(s32 interrupt_id, s32 core_id);
void Unregister(s32 interrupt_id);
public:
static Result Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event);
static Result Register(s32 interrupt_id, bool level, KInterruptEvent *event);
};
}

View File

@@ -24,59 +24,40 @@ namespace ams::kern {
class KLightConditionVariable {
private:
KThread::WaiterList m_wait_list;
KThreadQueue m_thread_queue;
public:
constexpr ALWAYS_INLINE KLightConditionVariable() : m_wait_list() { /* ... */ }
constexpr ALWAYS_INLINE KLightConditionVariable() : m_thread_queue() { /* ... */ }
private:
void WaitImpl(KLightLock *lock, s64 timeout, bool allow_terminating_thread) {
void WaitImpl(KLightLock *lock, s64 timeout) {
KThread *owner = GetCurrentThreadPointer();
KHardwareTimer *timer;
/* Sleep the thread. */
{
KScopedSchedulerLockAndSleep lk(&timer, owner, timeout);
lock->Unlock();
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
if (!m_thread_queue.SleepThread(owner)) {
lk.CancelSleep();
return;
}
lock->Unlock();
/* Set the thread as waiting. */
GetCurrentThread().SetState(KThread::ThreadState_Waiting);
/* Add the thread to the queue. */
m_wait_list.push_back(GetCurrentThread());
}
/* Remove the thread from the wait list. */
{
KScopedSchedulerLock sl;
m_wait_list.erase(m_wait_list.iterator_to(GetCurrentThread()));
}
/* Cancel the task that the sleep setup. */
if (timer != nullptr) {
timer->CancelTask(owner);
}
/* Re-acquire the lock. */
lock->Lock();
}
public:
void Wait(KLightLock *lock, s64 timeout = -1ll, bool allow_terminating_thread = true) {
this->WaitImpl(lock, timeout, allow_terminating_thread);
void Wait(KLightLock *lock, s64 timeout = -1ll) {
this->WaitImpl(lock, timeout);
lock->Lock();
}
void Broadcast() {
KScopedSchedulerLock lk;
/* Signal all threads. */
for (auto &thread : m_wait_list) {
thread.SetState(KThread::ThreadState_Runnable);
while (m_thread_queue.WakeupFrontThread() != nullptr) {
/* We want to signal all threads, and so should continue waking up until there's nothing to wake. */
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
namespace ams::kern {
class KLinkedListNode : public util::IntrusiveListBaseNode<KLinkedListNode>, public KSlabAllocated<KLinkedListNode> {
private:
void *m_item;
public:
constexpr KLinkedListNode() : util::IntrusiveListBaseNode<KLinkedListNode>(), m_item(nullptr) { MESOSPHERE_ASSERT_THIS(); }
constexpr void Initialize(void *it) {
MESOSPHERE_ASSERT_THIS();
m_item = it;
}
constexpr void *GetItem() const {
return m_item;
}
};
static_assert(sizeof(KLinkedListNode) == sizeof(util::IntrusiveListNode) + sizeof(void *));
template<typename T>
class KLinkedList : private util::IntrusiveListBaseTraits<KLinkedListNode>::ListType {
private:
using BaseList = util::IntrusiveListBaseTraits<KLinkedListNode>::ListType;
public:
template<bool Const>
class Iterator;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = value_type *;
using const_pointer = const value_type *;
using reference = value_type &;
using const_reference = const value_type &;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
template<bool Const>
class Iterator {
private:
using BaseIterator = BaseList::Iterator<Const>;
friend class KLinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename KLinkedList::value_type;
using difference_type = typename KLinkedList::difference_type;
using pointer = typename std::conditional<Const, KLinkedList::const_pointer, KLinkedList::pointer>::type;
using reference = typename std::conditional<Const, KLinkedList::const_reference, KLinkedList::reference>::type;
private:
BaseIterator m_base_it;
public:
explicit Iterator(BaseIterator it) : m_base_it(it) { /* ... */ }
pointer GetItem() const {
return static_cast<pointer>(m_base_it->GetItem());
}
bool operator==(const Iterator &rhs) const {
return m_base_it == rhs.m_base_it;
}
bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->GetItem();
}
reference operator*() const {
return *this->GetItem();
}
Iterator &operator++() {
++m_base_it;
return *this;
}
Iterator &operator--() {
--m_base_it;
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(m_base_it);
}
};
public:
constexpr KLinkedList() : BaseList() { /* ... */ }
~KLinkedList() {
/* Erase all elements. */
for (auto it = this->begin(); it != this->end(); it = this->erase(it)) {
/* ... */
}
/* Ensure we succeeded. */
MESOSPHERE_ASSERT(this->empty());
}
/* Iterator accessors. */
iterator begin() {
return iterator(BaseList::begin());
}
const_iterator begin() const {
return const_iterator(BaseList::begin());
}
iterator end() {
return iterator(BaseList::end());
}
const_iterator end() const {
return const_iterator(BaseList::end());
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
reverse_iterator rbegin() {
return reverse_iterator(this->end());
}
const_reverse_iterator rbegin() const {
return const_reverse_iterator(this->end());
}
reverse_iterator rend() {
return reverse_iterator(this->begin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(this->begin());
}
const_reverse_iterator crbegin() const {
return this->rbegin();
}
const_reverse_iterator crend() const {
return this->rend();
}
/* Content management. */
using BaseList::empty;
using BaseList::size;
reference back() {
return *(--this->end());
}
const_reference back() const {
return *(--this->end());
}
reference front() {
return *this->begin();
}
const_reference front() const {
return *this->begin();
}
iterator insert(const_iterator pos, reference ref) {
KLinkedListNode *node = KLinkedListNode::Allocate();
MESOSPHERE_ABORT_UNLESS(node != nullptr);
node->Initialize(std::addressof(ref));
return iterator(BaseList::insert(pos.m_base_it, *node));
}
void push_back(reference ref) {
this->insert(this->end(), ref);
}
void push_front(reference ref) {
this->insert(this->begin(), ref);
}
void pop_back() {
this->erase(--this->end());
}
void pop_front() {
this->erase(this->begin());
}
iterator erase(const iterator pos) {
KLinkedListNode *freed_node = std::addressof(*pos.m_base_it);
iterator ret = iterator(BaseList::erase(pos.m_base_it));
KLinkedListNode::Free(freed_node);
return ret;
}
};
}

View File

@@ -27,8 +27,18 @@ namespace ams::kern {
KMemoryBlock *m_blocks[MaxBlocks];
size_t m_index;
KMemoryBlockSlabManager *m_slab_manager;
private:
ALWAYS_INLINE Result Initialize(size_t num_blocks) {
public:
constexpr explicit KMemoryBlockManagerUpdateAllocator(KMemoryBlockSlabManager *sm) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) { /* ... */ }
~KMemoryBlockManagerUpdateAllocator() {
for (const auto &block : m_blocks) {
if (block != nullptr) {
m_slab_manager->Free(block);
}
}
}
Result Initialize(size_t num_blocks) {
/* Check num blocks. */
MESOSPHERE_ASSERT(num_blocks <= MaxBlocks);
@@ -43,18 +53,6 @@ namespace ams::kern {
return ResultSuccess();
}
public:
KMemoryBlockManagerUpdateAllocator(Result *out_result, KMemoryBlockSlabManager *sm, size_t num_blocks = MaxBlocks) : m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) {
*out_result = this->Initialize(num_blocks);
}
~KMemoryBlockManagerUpdateAllocator() {
for (const auto &block : m_blocks) {
if (block != nullptr) {
m_slab_manager->Free(block);
}
}
}
KMemoryBlock *Allocate() {
MESOSPHERE_ABORT_UNLESS(m_index < MaxBlocks);

View File

@@ -44,8 +44,9 @@ namespace ams::kern {
constexpr size_t KernelInitialPageHeapSize = 128_KB;
constexpr size_t KernelSlabHeapDataSize = 5_MB;
constexpr size_t KernelSlabHeapGapsSizeMax = 2_MB - 64_KB;
constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
constexpr size_t KernelSlabHeapGapsSize = 2_MB - 64_KB;
constexpr size_t KernelSlabHeapGapsSizeDeprecated = 2_MB;
constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
/* NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. */
constexpr size_t KernelSlabHeapAdditionalSize = 0x68000;

View File

@@ -75,7 +75,7 @@ namespace ams::kern {
KVirtualAddress AllocateBlock(s32 index, bool random) { return m_heap.AllocateBlock(index, random); }
void Free(KVirtualAddress addr, size_t num_pages) { m_heap.Free(addr, num_pages); }
void SetInitialUsedHeapSize(size_t reserved_size) { m_heap.SetInitialUsedSize(reserved_size); }
void UpdateUsedHeapSize() { m_heap.UpdateUsedSize(); }
void InitializeOptimizedMemory() { std::memset(GetVoidPointer(m_management_region), 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize())); }
@@ -168,10 +168,6 @@ namespace ams::kern {
return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()];
}
const Impl &GetManager(KVirtualAddress address) const {
return m_managers[KMemoryLayout::GetVirtualLinearRegion(address).GetAttributes()];
}
constexpr Impl *GetFirstManager(Pool pool, Direction dir) {
return dir == Direction_FromBack ? m_pool_managers_tail[pool] : m_pool_managers_head[pool];
}
@@ -201,10 +197,6 @@ namespace ams::kern {
NOINLINE Result AllocateAndOpen(KPageGroup *out, size_t num_pages, u32 option);
NOINLINE Result AllocateAndOpenForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern);
Pool GetPool(KVirtualAddress address) const {
return this->GetManager(address).GetPool();
}
void Open(KVirtualAddress address, size_t num_pages) {
/* Repeatedly open references until we've done so for all pages. */
while (num_pages) {

View File

@@ -21,8 +21,7 @@ namespace ams::kern {
enum KMemoryRegionType : u32 {};
enum KMemoryRegionAttr : typename std::underlying_type<KMemoryRegionType>::type {
KMemoryRegionAttr_CarveoutProtected = 0x02000000,
KMemoryRegionAttr_Uncached = 0x04000000,
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
@@ -217,10 +216,6 @@ namespace ams::kern {
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap .GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
/* UNUSED: .DeriveSparse(2, 2, 0); */
constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug = KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
constexpr inline const auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
@@ -297,8 +292,6 @@ namespace ams::kern {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
return KMemoryRegionType_VirtualDramUnknownDebug;
} else {
return KMemoryRegionType_Dram;
}

View File

@@ -47,9 +47,6 @@ namespace ams::kern {
Derived *derived = obj->DynamicCast<Derived *>();
R_UNLESS(derived != nullptr, svc::ResultNotFound());
/* Check that the object is closed. */
R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState());
return Delete(obj.GetPointerUnsafe(), name);
}

View File

@@ -125,7 +125,7 @@ namespace ams::kern {
private:
KVirtualAddress m_heap_address;
size_t m_heap_size;
size_t m_initial_used_size;
size_t m_used_size;
size_t m_num_blocks;
Block m_blocks[NumMemoryBlockPageShifts];
private:
@@ -134,7 +134,7 @@ namespace ams::kern {
void FreeBlock(KVirtualAddress block, s32 index);
public:
KPageHeap() : m_heap_address(), m_heap_size(), m_initial_used_size(), m_num_blocks(), m_blocks() { /* ... */ }
KPageHeap() : m_heap_address(), m_heap_size(), m_used_size(), m_num_blocks(), m_blocks() { /* ... */ }
constexpr KVirtualAddress GetAddress() const { return m_heap_address; }
constexpr size_t GetSize() const { return m_heap_size; }
@@ -149,13 +149,8 @@ namespace ams::kern {
size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; }
void DumpFreeList() const;
void SetInitialUsedSize(size_t reserved_size) {
/* Check that the reserved size is valid. */
const size_t free_size = this->GetNumFreePages() * PageSize;
MESOSPHERE_ABORT_UNLESS(m_heap_size >= free_size + reserved_size);
/* Set the initial used size. */
m_initial_used_size = m_heap_size - free_size - reserved_size;
void UpdateUsedSize() {
m_used_size = m_heap_size - (this->GetNumFreePages() * PageSize);
}
KVirtualAddress AllocateBlock(s32 index, bool random);

View File

@@ -47,21 +47,12 @@ namespace ams::kern {
static_assert(std::is_trivial<KPageProperties>::value);
static_assert(sizeof(KPageProperties) == sizeof(u32));
class KResourceLimit;
class KPageTableBase {
NON_COPYABLE(KPageTableBase);
NON_MOVEABLE(KPageTableBase);
public:
using TraversalEntry = KPageTableImpl::TraversalEntry;
using TraversalContext = KPageTableImpl::TraversalContext;
struct MemoryRange {
KVirtualAddress address;
size_t size;
void Close();
};
protected:
enum MemoryFillValue {
MemoryFillValue_Zero = 0,
@@ -159,10 +150,8 @@ namespace ams::kern {
size_t m_max_heap_size{};
size_t m_mapped_physical_memory_size{};
size_t m_mapped_unsafe_physical_memory{};
size_t m_mapped_ipc_server_memory{};
mutable KLightLock m_general_lock{};
mutable KLightLock m_map_physical_memory_lock{};
KLightLock m_device_map_lock{};
KPageTableImpl m_impl{};
KMemoryBlockManager m_memory_block_manager{};
u32 m_allocate_option{};
@@ -172,7 +161,6 @@ namespace ams::kern {
bool m_enable_device_address_space_merge{};
KMemoryBlockSlabManager *m_memory_block_slab_manager{};
KBlockInfoManager *m_block_info_manager{};
KResourceLimit *m_resource_limit{};
const KMemoryRegion *m_cached_physical_linear_region{};
const KMemoryRegion *m_cached_physical_heap_region{};
const KMemoryRegion *m_cached_virtual_heap_region{};
@@ -183,7 +171,7 @@ namespace ams::kern {
constexpr KPageTableBase() { /* ... */ }
NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit);
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager);
void Finalize();
@@ -207,10 +195,6 @@ namespace ams::kern {
return this->CanContain(addr, size, KMemoryState_AliasCode);
}
ALWAYS_INLINE KScopedLightLock AcquireDeviceMapLock() {
return KScopedLightLock(m_device_map_lock);
}
KProcessAddress GetRegionAddress(KMemoryState state) const;
size_t GetRegionSize(KMemoryState state) const;
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
@@ -302,35 +286,16 @@ namespace ams::kern {
Result MakePageGroup(KPageGroup &pg, KProcessAddress addr, size_t num_pages);
bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages);
Result GetContiguousMemoryRangeWithState(MemoryRange *out, KProcessAddress address, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr);
NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
Result MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
Result ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size);
Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size);
Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm);
size_t GetSize(KMemoryState state) const;
ALWAYS_INLINE bool GetPhysicalAddressLocked(KPhysicalAddress *out, KProcessAddress virt_addr) const {
/* Validate pre-conditions. */
MESOSPHERE_AUDIT(this->IsLockedByCurrentThread());
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
}
public:
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const {
/* Validate pre-conditions. */
MESOSPHERE_AUDIT(!this->IsLockedByCurrentThread());
/* Acquire exclusive access to the table while doing address translation. */
KScopedLightLock lk(m_general_lock);
return this->GetPhysicalAddressLocked(out, virt_addr);
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
}
KBlockInfoManager *GetBlockInfoManager() const { return m_block_info_manager; }
@@ -376,19 +341,13 @@ namespace ams::kern {
Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size);
Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size);
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size);
Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size);
Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size);
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size);
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange *out, KProcessAddress address, size_t size);
Result MakePageGroupForUnmapDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size);
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size, size_t mapped_size);
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size);
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
@@ -398,8 +357,6 @@ namespace ams::kern {
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size);
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
Result OpenMemoryRangeForProcessCacheOperation(MemoryRange *out, KProcessAddress address, size_t size);
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
Result CopyMemoryFromLinearToKernel(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr);
@@ -408,7 +365,7 @@ namespace ams::kern {
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
Result MapPhysicalMemory(KProcessAddress address, size_t size);
@@ -417,8 +374,6 @@ namespace ams::kern {
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase &src_pt, KProcessAddress src_address);
void DumpMemoryBlocksLocked() const {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
m_memory_block_manager.DumpBlocks();

View File

@@ -53,11 +53,6 @@ namespace ams::kern {
uintptr_t GetName() const { return m_name; }
bool IsLight() const { return m_is_light; }
bool IsServerClosed() const {
KScopedSchedulerLock sl;
return m_state == State::ServerClosed;
}
Result EnqueueSession(KServerSession *session);
Result EnqueueSession(KLightServerSession *session);

View File

@@ -77,7 +77,8 @@ namespace ams::kern {
bool m_is_initialized{};
bool m_is_application{};
char m_name[13]{};
std::atomic<u16> m_num_running_threads{};
std::atomic<u16> m_num_threads{};
u16 m_peak_num_threads{};
u32 m_flags{};
KMemoryManager::Pool m_memory_pool{};
s64 m_schedule_count{};
@@ -98,9 +99,7 @@ namespace ams::kern {
SharedMemoryInfoList m_shared_memory_list{};
BetaList m_beta_list{};
bool m_is_suspended{};
bool m_is_immortal{};
bool m_is_jit_debug{};
bool m_is_handle_table_initialized{};
ams::svc::DebugEvent m_jit_debug_event_type{};
ams::svc::DebugException m_jit_debug_exception_type{};
uintptr_t m_jit_debug_params[4]{};
@@ -109,6 +108,7 @@ namespace ams::kern {
KThread *m_running_threads[cpu::NumCores]{};
u64 m_running_thread_idle_counts[cpu::NumCores]{};
KThread *m_pinned_threads[cpu::NumCores]{};
std::atomic<s32> m_num_created_threads{};
std::atomic<s64> m_cpu_time{};
std::atomic<s64> m_num_process_switches{};
std::atomic<s64> m_num_thread_switches{};
@@ -124,17 +124,17 @@ namespace ams::kern {
private:
Result Initialize(const ams::svc::CreateProcessParameter &params);
Result StartTermination();
void StartTermination();
void FinishTermination();
ALWAYS_INLINE void PinThread(s32 core_id, KThread *thread) {
void PinThread(s32 core_id, KThread *thread) {
MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast<s32>(cpu::NumCores));
MESOSPHERE_ASSERT(thread != nullptr);
MESOSPHERE_ASSERT(m_pinned_threads[core_id] == nullptr);
m_pinned_threads[core_id] = thread;
}
ALWAYS_INLINE void UnpinThread(s32 core_id, KThread *thread) {
void UnpinThread(s32 core_id, KThread *thread) {
MESOSPHERE_UNUSED(thread);
MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast<s32>(cpu::NumCores));
MESOSPHERE_ASSERT(thread != nullptr);
@@ -145,7 +145,7 @@ namespace ams::kern {
KProcess() { /* ... */ }
virtual ~KProcess() { /* ... */ }
Result Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal);
Result Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool);
Result Initialize(const ams::svc::CreateProcessParameter &params, svc::KUserPointer<const u32 *> caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool);
void Exit();
@@ -285,8 +285,8 @@ namespace ams::kern {
constexpr s64 GetScheduledCount() const { return m_schedule_count; }
void IncrementScheduledCount() { ++m_schedule_count; }
void IncrementRunningThreadCount();
void DecrementRunningThreadCount();
void IncrementThreadCount();
void DecrementThreadCount();
size_t GetTotalSystemResourceSize() const { return m_system_resource_num_pages * PageSize; }
size_t GetUsedSystemResourceSize() const {
@@ -340,7 +340,6 @@ namespace ams::kern {
void PinCurrentThread();
void UnpinCurrentThread();
void UnpinThread(KThread *thread);
Result SignalToAddress(KProcessAddress address) {
return m_cond_var.SignalToAddress(address);
@@ -406,23 +405,6 @@ namespace ams::kern {
this->NotifyAvailable();
}
}
ALWAYS_INLINE Result InitializeHandleTable(s32 size) {
/* Try to initialize the handle table. */
R_TRY(m_handle_table.Initialize(size));
/* We succeeded, so note that we did. */
m_is_handle_table_initialized = true;
return ResultSuccess();
}
ALWAYS_INLINE void FinalizeHandleTable() {
/* Finalize the table. */
m_handle_table.Finalize();
/* Note that the table is finalized. */
m_is_handle_table_initialized = false;
}
};
}

View File

@@ -37,22 +37,14 @@ namespace ams::kern {
private:
KServerSession m_server;
KClientSession m_client;
std::atomic<std::underlying_type<State>::type> m_atomic_state;
State m_state;
KClientPort *m_port;
uintptr_t m_name;
KProcess *m_process;
bool m_initialized;
private:
ALWAYS_INLINE void SetState(State state) {
m_atomic_state = static_cast<u8>(state);
}
ALWAYS_INLINE State GetState() const {
return static_cast<State>(m_atomic_state.load());
}
public:
constexpr KSession()
: m_server(), m_client(), m_atomic_state(static_cast<std::underlying_type<State>::type>(State::Invalid)), m_port(), m_name(), m_process(), m_initialized()
: m_server(), m_client(), m_state(State::Invalid), m_port(), m_name(), m_process(), m_initialized()
{
/* ... */
}
@@ -70,8 +62,8 @@ namespace ams::kern {
void OnServerClosed();
void OnClientClosed();
bool IsServerClosed() const { return this->GetState() != State::Normal; }
bool IsClientClosed() const { return this->GetState() != State::Normal; }
bool IsServerClosed() const { return m_state != State::Normal; }
bool IsClientClosed() const { return m_state != State::Normal; }
Result OnRequest(KSessionRequest *request) { return m_server.OnRequest(request); }

View File

@@ -216,7 +216,7 @@ namespace ams::kern {
T *Allocate() {
T *obj = reinterpret_cast<T *>(this->AllocateImpl());
if (AMS_LIKELY(obj != nullptr)) {
std::construct_at(obj);
new (obj) T();
}
return obj;
}

View File

@@ -16,6 +16,7 @@
#pragma once
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_linked_list.hpp>
namespace ams::kern {
@@ -45,7 +46,7 @@ namespace ams::kern {
static Result Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout);
public:
virtual void Finalize() override;
virtual bool IsSignaled() const { AMS_INFINITE_LOOP(); }
virtual bool IsSignaled() const = 0;
virtual void DumpWaiters();
};

View File

@@ -77,34 +77,22 @@ namespace ams::kern {
};
enum DpcFlag : u32 {
DpcFlag_Terminating = (1 << 0),
DpcFlag_Terminated = (1 << 1),
DpcFlag_PerformDestruction = (1 << 2),
DpcFlag_Terminating = (1 << 0),
DpcFlag_Terminated = (1 << 1),
};
struct StackParameters {
alignas(0x10) u8 svc_permission[0x18];
KThreadContext *context;
KThread *cur_thread;
s16 disable_count;
alignas(0x10) u8 svc_permission[0x10];
std::atomic<u8> dpc_flags;
u8 current_svc_id;
bool is_calling_svc;
bool is_in_exception_handler;
bool is_pinned;
s32 disable_count;
KThreadContext *context;
KThread *cur_thread;
};
static_assert(alignof(StackParameters) == 0x10);
static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE);
static_assert(__builtin_offsetof(StackParameters, svc_permission) == THREAD_STACK_PARAMETERS_SVC_PERMISSION);
static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT);
static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD);
static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT);
static_assert(__builtin_offsetof(StackParameters, dpc_flags) == THREAD_STACK_PARAMETERS_DPC_FLAGS);
static_assert(__builtin_offsetof(StackParameters, current_svc_id) == THREAD_STACK_PARAMETERS_CURRENT_SVC_ID);
static_assert(__builtin_offsetof(StackParameters, is_calling_svc) == THREAD_STACK_PARAMETERS_IS_CALLING_SVC);
static_assert(__builtin_offsetof(StackParameters, is_in_exception_handler) == THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER);
static_assert(__builtin_offsetof(StackParameters, is_pinned) == THREAD_STACK_PARAMETERS_IS_PINNED);
struct QueueEntry {
private:
@@ -204,14 +192,12 @@ namespace ams::kern {
WaiterList m_pinned_waiter_list{};
KThread *m_lock_owner{};
uintptr_t m_debug_params[3]{};
KAutoObject *m_closed_object{};
u32 m_address_key_value{};
u32 m_suspend_request_flags{};
u32 m_suspend_allowed_flags{};
Result m_wait_result;
Result m_debug_exception_result;
s32 m_base_priority{};
s32 m_base_priority_on_unpin{};
s32 m_physical_ideal_core_id{};
s32 m_virtual_ideal_core_id{};
s32 m_num_kernel_waiters{};
@@ -265,7 +251,7 @@ namespace ams::kern {
return *(reinterpret_cast<StackParameters *>(m_kernel_stack_top) - 1);
}
public:
ALWAYS_INLINE s16 GetDisableDispatchCount() const {
ALWAYS_INLINE s32 GetDisableDispatchCount() const {
MESOSPHERE_ASSERT_THIS();
return this->GetStackParameters().disable_count;
}
@@ -326,15 +312,15 @@ namespace ams::kern {
}
ALWAYS_INLINE void RegisterDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags.fetch_or(flag);
this->GetStackParameters().dpc_flags |= flag;
}
ALWAYS_INLINE void ClearDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags.fetch_and(~flag);
this->GetStackParameters().dpc_flags &= ~flag;
}
ALWAYS_INLINE u8 GetDpc() const {
return this->GetStackParameters().dpc_flags.load();
return this->GetStackParameters().dpc_flags;
}
ALWAYS_INLINE bool HasDpc() const {
@@ -342,15 +328,13 @@ namespace ams::kern {
return this->GetDpc() != 0;
}
private:
void UpdateState();
void Suspend();
ALWAYS_INLINE void AddWaiterImpl(KThread *thread);
ALWAYS_INLINE void RemoveWaiterImpl(KThread *thread);
ALWAYS_INLINE static void RestorePriority(KThread *thread);
void StartTermination();
void FinishTermination();
void IncreaseBasePriority(s32 priority);
public:
constexpr u64 GetThreadId() const { return m_thread_id; }
@@ -493,39 +477,6 @@ namespace ams::kern {
void SetInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 1; }
void ClearInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 0; }
ALWAYS_INLINE KAutoObject *GetClosedObject() { return m_closed_object; }
ALWAYS_INLINE void SetClosedObject(KAutoObject *object) {
MESOSPHERE_ASSERT(object != nullptr);
/* Set the object to destroy. */
m_closed_object = object;
/* Schedule destruction DPC. */
if ((this->GetStackParameters().dpc_flags.load(std::memory_order_relaxed) & DpcFlag_PerformDestruction) == 0) {
this->RegisterDpc(DpcFlag_PerformDestruction);
}
}
ALWAYS_INLINE void DestroyClosedObjects() {
/* Destroy all objects that have been closed. */
if (KAutoObject *cur = m_closed_object; cur != nullptr) {
do {
/* Set our closed object as the next to close. */
m_closed_object = cur->GetNextClosedObject();
/* Destroy the current object. */
cur->Destroy();
/* Advance. */
cur = m_closed_object;
} while (cur != nullptr);
/* Clear the pending DPC. */
this->ClearDpc(DpcFlag_PerformDestruction);
}
}
constexpr void SetDebugAttached() { m_debug_attached = true; }
constexpr bool IsAttachedToDebugger() const { return m_debug_attached; }
@@ -570,7 +521,7 @@ namespace ams::kern {
Result Run();
void Exit();
Result Terminate();
void Terminate();
ThreadState RequestTerminate();
Result Sleep(s64 timeout);
@@ -638,14 +589,4 @@ namespace ams::kern {
return GetCurrentThread().GetCurrentCore();
}
ALWAYS_INLINE void KAutoObject::ScheduleDestruction() {
MESOSPHERE_ASSERT_THIS();
/* Set our object to destroy. */
m_next_closed_object = GetCurrentThread().GetClosedObject();
/* Set ourselves as the thread's next object to destroy. */
GetCurrentThread().SetClosedObject(this);
}
}

View File

@@ -23,7 +23,7 @@ namespace ams::kern {
class KTransferMemory final : public KAutoObjectWithSlabHeapAndContainer<KTransferMemory, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KTransferMemory, KAutoObject);
private:
util::TypedStorage<KPageGroup> m_page_group;
TYPED_STORAGE(KPageGroup) m_page_group;
KProcess *m_owner;
KProcessAddress m_address;
KLightLock m_lock;

View File

@@ -1,26 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef ATMOSPHERE_ARCH_ARM64
#include <mesosphere/arch/arm64/kern_assembly_offsets.h>
#else
#error "Unknown architecture for CPU"
#endif

View File

@@ -45,21 +45,7 @@
namespace ams::kern {
namespace cpu {
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
static constexpr inline u64 VirtualCoreMask = [] {
u64 mask = 0;
for (size_t i = 0; i < NumVirtualCores; ++i) {
mask |= (UINT64_C(1) << i);
}
return mask;
}();
}
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
}

View File

@@ -20,7 +20,7 @@
namespace ams::kern::svc {
static constexpr size_t NumSupervisorCalls = 0xC0;
static constexpr size_t NumSupervisorCalls = 0x80;
#define AMS_KERN_SVC_DECLARE_ENUM_ID(ID, RETURN_TYPE, NAME, ...) \
SvcId_##NAME = ID,

View File

@@ -283,16 +283,6 @@ namespace ams::kern::arch::arm64::cpu {
}
}
void StoreDataCacheBySetWay(int level) {
PerformCacheOperationBySetWayImpl<false>(level, StoreDataCacheLineBySetWayImpl);
cpu::DataSynchronizationBarrier();
}
void FlushDataCacheBySetWay(int level) {
PerformCacheOperationBySetWayImpl<false>(level, FlushDataCacheLineBySetWayImpl);
cpu::DataSynchronizationBarrier();
}
void KCacheHelperInterruptHandler::ProcessOperation() {
switch (m_operation) {
case Operation::Idle:
@@ -301,10 +291,12 @@ namespace ams::kern::arch::arm64::cpu {
InstructionMemoryBarrier();
break;
case Operation::StoreDataCache:
StoreDataCacheBySetWay(0);
PerformCacheOperationBySetWayLocal<false>(StoreDataCacheLineBySetWayImpl);
DataSynchronizationBarrier();
break;
case Operation::FlushDataCache:
FlushDataCacheBySetWay(0);
PerformCacheOperationBySetWayLocal<false>(FlushDataCacheLineBySetWayImpl);
DataSynchronizationBarrier();
break;
}
@@ -382,20 +374,7 @@ namespace ams::kern::arch::arm64::cpu {
}
void FlushEntireDataCache() {
KScopedCoreMigrationDisable dm;
CacheLineIdRegisterAccessor clidr_el1;
const int levels_of_coherency = clidr_el1.GetLevelsOfCoherency();
/* Store cache from L2 up to the level of coherence (if there's an L3 cache or greater). */
for (int level = 2; level < levels_of_coherency; ++level) {
StoreDataCacheBySetWay(level - 1);
}
/* Flush cache from the level of coherence down to L2. */
for (int level = levels_of_coherency; level > 1; --level) {
FlushDataCacheBySetWay(level - 1);
}
return PerformCacheOperationBySetWayShared<false>(FlushDataCacheLineBySetWayImpl);
}
Result InvalidateDataCache(void *addr, size_t size) {

View File

@@ -521,11 +521,6 @@ namespace ams::kern::arch::arm64 {
{
KScopedInterruptEnable ei;
/* Terminate the thread, if we should. */
if (GetCurrentThread().IsTerminationRequested()) {
GetCurrentThread().Exit();
}
HandleUserException(context, esr, far, afsr0, afsr1, data);
}
} else {

View File

@@ -239,6 +239,14 @@ namespace ams::kern::arch::arm64 {
}
}
Result KInterruptManager::ClearInterrupt(s32 irq) {
R_UNLESS(KInterruptController::IsGlobal(irq), svc::ResultOutOfRange());
KScopedInterruptDisable di;
KScopedSpinLock lk(this->GetGlobalInterruptLock());
return this->ClearGlobal(irq);
}
Result KInterruptManager::ClearInterrupt(s32 irq, s32 core_id) {
MESOSPHERE_UNUSED(core_id);

View File

@@ -181,7 +181,7 @@ namespace ams::kern::arch::arm64 {
return ResultSuccess();
}
Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) {
Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
/* The input ID isn't actually used. */
MESOSPHERE_UNUSED(id);
@@ -202,7 +202,7 @@ namespace ams::kern::arch::arm64 {
const size_t as_width = GetAddressSpaceWidth(as_type);
const KProcessAddress as_start = 0;
const KProcessAddress as_end = (1ul << as_width);
R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager, resource_limit));
R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager));
/* We succeeded! */
table_guard.Cancel();
@@ -556,13 +556,13 @@ namespace ams::kern::arch::arm64 {
/* If we're not forcing an unmap, separate pages immediately. */
if (!force) {
const size_t size = num_pages * PageSize;
R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll));
R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll));
if (num_pages > 1) {
const auto end_page = virt_addr + size;
const auto last_page = end_page - PageSize;
auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); };
R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll));
R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll));
merge_guard.Cancel();
}
}
@@ -1194,13 +1194,13 @@ namespace ams::kern::arch::arm64 {
/* Separate pages before we change permissions. */
const size_t size = num_pages * PageSize;
R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll));
R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll));
if (num_pages > 1) {
const auto end_page = virt_addr + size;
const auto last_page = end_page - PageSize;
auto merge_guard = SCOPE_GUARD { this->MergePages(virt_addr, page_list); };
R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll));
R_TRY(this->SeparatePages(last_page, std::min(GetInteger(end_page) & -GetInteger(end_page), size), page_list, reuse_ll));
merge_guard.Cancel();
}

View File

@@ -758,6 +758,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm:
mov x8, x30
1: /* Read the word from normal memory. */
mov x30, x8
ldtr w9, [x5]
/* Set our return address so that on read failure we continue. */
@@ -768,7 +769,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm:
dsb sy
2: /* Continue. */
mov x30, x8
nop
/* Advance. */
add x4, x4, #4
@@ -800,6 +801,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm:
mov x8, x30
1: /* Read the word from normal memory. */
mov x30, x8
ldtrh w9, [x5]
/* Set our return address so that on read failure we continue. */
@@ -810,7 +812,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm:
dsb sy
2: /* Continue. */
mov x30, x8
nop
/* Advance. */
add x4, x4, #2
@@ -842,6 +844,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm:
mov x8, x30
1: /* Read the word from normal memory. */
mov x30, x8
ldtrb w9, [x5]
/* Set our return address so that on read failure we continue. */
@@ -852,7 +855,7 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm:
dsb sy
2: /* Continue. */
mov x30, x8
nop
/* Advance. */
add x4, x4, #1

View File

@@ -13,7 +13,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mesosphere/kern_select_assembly_offsets.h>
/* ams::kern::svc::CallReturnFromException64(Result result) */
.section .text._ZN3ams4kern3svc25CallReturnFromException64Ev, "ax", %progbits
@@ -63,7 +62,7 @@ _ZN3ams4kern3svc14RestoreContextEm:
0: /* We should handle DPC. */
/* Check the dpc flags. */
ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
ldrb w8, [sp, #(0x120 + 0x10)]
cbz w8, 1f
/* We have DPC to do! */
@@ -83,7 +82,7 @@ _ZN3ams4kern3svc14RestoreContextEm:
1: /* We're done with DPC, and should return from the svc. */
/* Clear our in-SVC note. */
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
strb wzr, [sp, #(0x120 + 0x12)]
/* Restore registers. */
ldp x30, x8, [sp, #(8 * 30)]

View File

@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mesosphere/kern_build_config.hpp>
#include <mesosphere/kern_select_assembly_offsets.h>
/* ams::kern::arch::arm64::SvcHandler64() */
.section .text._ZN3ams4kern4arch5arm6412SvcHandler64Ev, "ax", %progbits
@@ -33,7 +32,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
mrs x9, elr_el1
mrs x10, spsr_el1
mrs x11, tpidr_el0
ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)]
ldr x18, [sp, #(0x120 + 0x28)]
/* Save callee-saved registers. */
stp x19, x20, [sp, #(8 * 19)]
@@ -60,7 +59,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
/* Check the specific SVC permission bit for allowal. */
mov x9, sp
add x9, x9, x8, lsr#3
ldrb w9, [x9, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
ldrb w9, [x9, #0x120]
and x10, x8, #0x7
lsr x10, x9, x10
tst x10, #1
@@ -72,7 +71,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
cbz w10, 1f
/* It might not, so check the stack params to see if we must not allow the SVC. */
ldrb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)]
ldrb w10, [sp, #(0x120 + 0x14)]
cbz w10, 3f
1: /* We can call the SVC. */
@@ -82,8 +81,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
/* Note that we're calling the SVC. */
mov w10, #1
strb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
strb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
strb w10, [sp, #(0x120 + 0x12)]
strb w8, [sp, #(0x120 + 0x11)]
/* If we should, trace the svc entry. */
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
@@ -110,7 +109,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
2: /* We completed the SVC, and we should handle DPC. */
/* Check the dpc flags. */
ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
ldrb w8, [sp, #(0x120 + 0x10)]
cbz w8, 4f
/* We have DPC to do! */
@@ -180,7 +179,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
4: /* Return from SVC. */
/* Clear our in-SVC note. */
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
strb wzr, [sp, #(0x120 + 0x12)]
/* If we should, trace the svc exit. */
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
@@ -246,7 +245,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
mrs x17, elr_el1
mrs x20, spsr_el1
mrs x19, tpidr_el0
ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)]
ldr x18, [sp, #(0x120 + 0x28)]
stp x17, x20, [sp, #(8 * 32)]
str x19, [sp, #(8 * 34)]
@@ -269,7 +268,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
/* Check the specific SVC permission bit for allowal. */
mov x20, sp
add x20, x20, x16, lsr#3
ldrb w20, [x20, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
ldrb w20, [x20, #0x120]
and x17, x16, #0x7
lsr x17, x20, x17
tst x17, #1
@@ -281,7 +280,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
cbz w15, 1f
/* It might not, so check the stack params to see if we must not allow the SVC. */
ldrb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)]
ldrb w15, [sp, #(0x120 + 0x14)]
cbz w15, 3f
1: /* We can call the SVC. */
@@ -291,8 +290,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
/* Note that we're calling the SVC. */
mov w15, #1
strb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
strb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
strb w15, [sp, #(0x120 + 0x12)]
strb w16, [sp, #(0x120 + 0x11)]
/* If we should, trace the svc entry. */
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
@@ -319,7 +318,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
2: /* We completed the SVC, and we should handle DPC. */
/* Check the dpc flags. */
ldrb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
ldrb w16, [sp, #(0x120 + 0x10)]
cbz w16, 4f
/* We have DPC to do! */
@@ -377,7 +376,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
4: /* Return from SVC. */
/* Clear our in-SVC note. */
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
strb wzr, [sp, #(0x120 + 0x12)]
/* If we should, trace the svc exit. */
#if defined(MESOSPHERE_BUILD_FOR_TRACING)

View File

@@ -216,12 +216,6 @@ namespace ams::kern::board::nintendo::nx {
return (m_value & (1u << n));
}
template<Bit... Bits>
constexpr ALWAYS_INLINE u32 SelectBits() const {
constexpr u32 Mask = ((1u << Bits) | ...);
return m_value & Mask;
}
constexpr ALWAYS_INLINE bool GetBit(Bit n) const {
return this->SelectBit(n) != 0;
}
@@ -248,14 +242,12 @@ namespace ams::kern::board::nintendo::nx {
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBit(Bit_NonSecure); }
constexpr ALWAYS_INLINE bool IsWriteable() const { return this->GetBit(Bit_Writeable); }
constexpr ALWAYS_INLINE bool IsReadable() const { return this->GetBit(Bit_Readable); }
constexpr ALWAYS_INLINE bool IsValid() const { return this->SelectBits<Bit_Readable, Bit_Writeable>(); }
constexpr ALWAYS_INLINE bool IsValid() const { return this->IsWriteable() || this->IsReadable(); }
constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBits<Bit_Readable, Bit_Writeable, Bit_NonSecure>(); }
constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); }
constexpr ALWAYS_INLINE KPhysicalAddress GetPhysicalAddress() const { return (static_cast<u64>(m_value) << DevicePageBits) & PhysicalAddressMask; }
ALWAYS_INLINE void InvalidateAttributes() { this->SetValue(m_value & ~(0xCu << 28)); }
ALWAYS_INLINE void Invalidate() { this->SetValue(0); }
};
@@ -534,7 +526,7 @@ namespace ams::kern::board::nintendo::nx {
#if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT)
{
/* Clear the interrupt when we're done. */
ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController, GetCurrentCoreId()); };
ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController); };
/* Get and clear the interrupt status. */
u32 int_status, err_status, err_adr;
@@ -855,7 +847,7 @@ namespace ams::kern::board::nintendo::nx {
}
/* Forcibly unmap all pages. */
this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), false);
this->UnmapImpl(0, (1ul << DeviceVirtualAddressBits), true);
/* Release all asids. */
for (size_t i = 0; i < TableCount; ++i) {
@@ -1125,11 +1117,12 @@ namespace ams::kern::board::nintendo::nx {
return ResultSuccess();
}
Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
Result KDevicePageTable::MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm) {
/* Clear the output size. */
*out_mapped_size = 0;
/* Get the size, and validate the address. */
const u64 size = pg.GetNumPages() * PageSize;
MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0);
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
@@ -1137,33 +1130,28 @@ namespace ams::kern::board::nintendo::nx {
R_UNLESS(this->IsFree(device_address, size), svc::ResultInvalidCurrentMemory());
/* Ensure that if we fail, we unmap anything we mapped. */
auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, false); };
auto unmap_guard = SCOPE_GUARD { this->UnmapImpl(device_address, size, true); };
/* Iterate, mapping device pages. */
KDeviceVirtualAddress cur_addr = device_address;
while (true) {
/* Get the current contiguous range. */
KPageTableBase::MemoryRange contig_range = {};
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + *out_mapped_size, size - *out_mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
for (auto it = pg.begin(); it != pg.end(); ++it) {
/* Require that we be able to map the device page. */
R_UNLESS(IsHeapVirtualAddress(it->GetAddress()), svc::ResultInvalidCurrentMemory());
/* Ensure we close the range when we're done. */
ON_SCOPE_EXIT { contig_range.Close(); };
/* Get the physical address for the page. */
const KPhysicalAddress phys_addr = GetHeapPhysicalAddress(it->GetAddress());
/* Map the device page. */
const u64 block_size = it->GetSize();
size_t mapped_size = 0;
R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, GetHeapPhysicalAddress(contig_range.address), contig_range.size, cur_addr, device_perm));
R_TRY(this->MapDevicePage(std::addressof(mapped_size), num_pt, max_pt, phys_addr, block_size, cur_addr, device_perm));
/* Advance. */
cur_addr += contig_range.size;
cur_addr += block_size;
*out_mapped_size += mapped_size;
/* If we didn't map as much as we wanted, break. */
if (mapped_size < contig_range.size) {
break;
}
/* Similarly, if we're done, break. */
if (*out_mapped_size >= size) {
if (mapped_size < block_size) {
break;
}
}
@@ -1198,6 +1186,8 @@ namespace ams::kern::board::nintendo::nx {
/* Check if there's nothing mapped at l1. */
if (l1 == nullptr || !l1[l1_index].IsValid()) {
MESOSPHERE_ASSERT(force);
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
@@ -1211,12 +1201,30 @@ namespace ams::kern::board::nintendo::nx {
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
size_t num_closed = 0;
bool invalidated_tlb = false;
/* Invalidate the attributes of all entries. */
for (size_t i = 0; i < map_count; ++i) {
if (l2[l2_index + i].IsValid()) {
l2[l2_index + i].InvalidateAttributes();
/* Get the physical address. */
const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress();
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
/* Invalidate the entry. */
l2[l2_index + i].Invalidate();
++num_closed;
/* Try to add the page to the group. */
if (R_FAILED(pg.AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize))) {
/* If we can't add it for deferred close, close it now. */
cpu::StoreDataCache(std::addressof(l2[l2_index + i]), sizeof(PageTableEntry));
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l2[l2_index + i]))));
SmmuSynchronizationBarrier();
/* Close the page's reference. */
mm.Close(GetHeapVirtualAddress(phys_addr), 1);
}
} else {
MESOSPHERE_ASSERT(force);
}
}
cpu::StoreDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry));
@@ -1227,38 +1235,6 @@ namespace ams::kern::board::nintendo::nx {
}
SmmuSynchronizationBarrier();
/* Close the memory manager's references to the pages. */
{
KPhysicalAddress contig_phys_addr = Null<KPhysicalAddress>;
size_t contig_count = 0;
for (size_t i = 0; i < map_count; ++i) {
/* Get the physical address. */
const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress();
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
/* Fully invalidate the entry. */
l2[l2_index + i].Invalidate();
if (contig_count == 0) {
/* Ensure that our address/count is valid. */
contig_phys_addr = phys_addr;
contig_count = contig_phys_addr != Null<KPhysicalAddress> ? 1 : 0;
} else if (phys_addr == Null<KPhysicalAddress> || phys_addr != (contig_phys_addr + (contig_count * DevicePageSize))) {
/* If we're no longer contiguous, close the range we've been building. */
mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize);
contig_phys_addr = phys_addr;
contig_count = contig_phys_addr != Null<KPhysicalAddress> ? 1 : 0;
} else {
++contig_count;
}
}
if (contig_count > 0) {
mm.Close(GetHeapVirtualAddress(contig_phys_addr), (contig_count * DevicePageSize) / PageSize);
}
}
/* Close the pages. */
if (ptm.Close(KVirtualAddress(l2), num_closed)) {
/* Invalidate the l1 entry. */
@@ -1267,12 +1243,22 @@ namespace ams::kern::board::nintendo::nx {
/* Synchronize. */
InvalidatePtc(GetPageTablePhysicalAddress(KVirtualAddress(std::addressof(l1[l1_index]))));
InvalidateTlbSection(m_table_asids[l0_index], address);
SmmuSynchronizationBarrier();
/* We invalidated the tlb. */
invalidated_tlb = true;
/* Free the l2 page. */
ptm.Free(KVirtualAddress(l2));
}
/* Invalidate the tlb if we haven't already. */
if (!invalidated_tlb) {
InvalidateTlbSection(m_table_asids[l0_index], address);
SmmuSynchronizationBarrier();
}
/* Advance. */
address += map_count * DevicePageSize;
remaining -= map_count * DevicePageSize;
@@ -1301,158 +1287,114 @@ namespace ams::kern::board::nintendo::nx {
remaining -= DeviceLargePageSize;
}
}
/* Close references to the pages in the group. */
pg.Close();
}
bool KDevicePageTable::Compare(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) const {
MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0);
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
KPageTableBase::MemoryRange contig_range = {};
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
return false;
}
/* Ensure that we close the range when we're done. */
bool range_open = true;
ON_SCOPE_EXIT { if (range_open) { contig_range.Close(); } };
Result KDevicePageTable::MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const {
MESOSPHERE_ASSERT((address & ~DeviceVirtualAddressMask) == 0);
MESOSPHERE_ASSERT(((address + size - 1) & ~DeviceVirtualAddressMask) == 0);
/* Walk the directory. */
KProcessAddress cur_process_address = process_address;
size_t remaining_size = size;
KPhysicalAddress cur_phys_address = GetHeapPhysicalAddress(contig_range.address);
size_t remaining_in_range = contig_range.size;
bool first = true;
u32 first_attr = 0;
while (remaining_size > 0) {
/* Convert the device address to a series of indices. */
const size_t l0_index = (device_address / DeviceRegionSize);
const size_t l1_index = (device_address % DeviceRegionSize) / DeviceLargePageSize;
const size_t l2_index = (device_address % DeviceLargePageSize) / DevicePageSize;
u64 remaining = size;
bool first = true;
u32 attr = 0;
while (remaining > 0) {
const size_t l0_index = (address / DeviceRegionSize);
const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize;
const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize;
/* Get and validate l1. */
const PageDirectoryEntry *l1 = GetPointer<PageDirectoryEntry>(m_tables[l0_index]);
if (!(l1 != nullptr && l1[l1_index].IsValid())) {
return false;
}
R_UNLESS(l1 != nullptr, svc::ResultInvalidCurrentMemory());
R_UNLESS(l1[l1_index].IsValid(), svc::ResultInvalidCurrentMemory());
if (l1[l1_index].IsTable()) {
/* We're acting on an l2 entry. */
const PageTableEntry *l2 = GetPointer<PageTableEntry>(GetPageTableVirtualAddress(l1[l1_index].GetPhysicalAddress()));
/* Determine the number of pages to check. */
const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index;
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining_size / DevicePageSize);
const size_t map_count = std::min<size_t>(remaining_in_entry, remaining / DevicePageSize);
/* Check each page. */
for (size_t i = 0; i < map_count; ++i) {
/* Ensure the l2 entry is valid. */
if (!l2[l2_index + i].IsValid()) {
return false;
R_UNLESS(l2[l2_index + i].IsValid(), svc::ResultInvalidCurrentMemory());
/* Get the physical address. */
const KPhysicalAddress phys_addr = l2[l2_index + i].GetPhysicalAddress();
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
/* Add to the group. */
R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DevicePageSize / PageSize));
/* If this is our first entry, get the attribute. */
if (first) {
attr = l2[l2_index + i].GetAttributes();
first = false;
} else {
/* Validate the attributes match the first entry. */
R_UNLESS(l2[l2_index + i].GetAttributes() == attr, svc::ResultInvalidCurrentMemory());
}
/* Check that the attributes match the first attributes we encountered. */
const u32 cur_attr = l2[l2_index + i].GetAttributes();
if (!first && cur_attr != first_attr) {
return false;
}
/* If there's nothing remaining in the range, refresh the range. */
if (remaining_in_range == 0) {
contig_range.Close();
range_open = false;
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) {
return false;
}
range_open = true;
cur_phys_address = GetHeapPhysicalAddress(contig_range.address);
remaining_in_range = contig_range.size;
}
/* Check that the physical address is expected. */
if (l2[l2_index + i].GetPhysicalAddress() != cur_phys_address) {
return false;
}
/* Advance. */
cur_phys_address += DevicePageSize;
cur_process_address += DevicePageSize;
remaining_size -= DevicePageSize;
remaining_in_range -= DevicePageSize;
first = false;
first_attr = cur_attr;
}
/* Advance the device address. */
device_address += map_count * DevicePageSize;
} else {
/* We're acting on an l1 entry. */
if (!(l2_index == 0 && remaining_size >= DeviceLargePageSize)) {
return false;
}
/* Check that the attributes match the first attributes we encountered. */
const u32 cur_attr = l1[l1_index].GetAttributes();
if (!first && cur_attr != first_attr) {
return false;
}
/* If there's nothing remaining in the range, refresh the range. */
if (remaining_in_range == 0) {
contig_range.Close();
range_open = false;
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), cur_process_address, remaining_size))) {
return false;
}
range_open = true;
cur_phys_address = GetHeapPhysicalAddress(contig_range.address);
remaining_in_range = contig_range.size;
}
/* Check that the physical address is expected, and there's enough in the range. */
if (remaining_in_range < DeviceLargePageSize || l1[l1_index].GetPhysicalAddress() != cur_phys_address) {
return false;
}
/* Advance. */
cur_phys_address += DeviceLargePageSize;
cur_process_address += DeviceLargePageSize;
remaining_size -= DeviceLargePageSize;
remaining_in_range -= DeviceLargePageSize;
address += DevicePageSize * map_count;
remaining -= DevicePageSize * map_count;
} else {
/* We're acting on an l1 entry. */
R_UNLESS(l2_index == 0, svc::ResultInvalidCurrentMemory());
R_UNLESS(remaining >= DeviceLargePageSize, svc::ResultInvalidCurrentMemory());
first = false;
first_attr = cur_attr;
/* Get the physical address. */
const KPhysicalAddress phys_addr = l1[l1_index].GetPhysicalAddress();
MESOSPHERE_ASSERT(IsHeapPhysicalAddress(phys_addr));
/* Advance the device address. */
device_address += DeviceLargePageSize;
/* Add to the group. */
R_TRY(out->AddBlock(GetHeapVirtualAddress(phys_addr), DeviceLargePageSize / PageSize));
/* If this is our first entry, get the attribute. */
if (first) {
attr = l1[l1_index].GetAttributes();
first = false;
} else {
/* Validate the attributes match the first entry. */
R_UNLESS(l1[l1_index].GetAttributes() == attr, svc::ResultInvalidCurrentMemory());
}
/* Advance. */
address += DeviceLargePageSize;
remaining -= DeviceLargePageSize;
}
}
/* The range is valid! */
return true;
return ResultSuccess();
}
Result KDevicePageTable::Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
bool KDevicePageTable::Compare(const KPageGroup &compare_pg, KDeviceVirtualAddress device_address) const {
/* Check whether the page group we expect for the virtual address matches the page group we're validating. */
KPageGroup calc_pg(std::addressof(Kernel::GetBlockInfoManager()));
return (R_SUCCEEDED(this->MakePageGroup(std::addressof(calc_pg), device_address, compare_pg.GetNumPages() * PageSize))) &&
calc_pg.IsEquivalentTo(compare_pg);
}
Result KDevicePageTable::Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
/* Clear the output size. */
*out_mapped_size = 0;
/* Map the pages. */
s32 num_pt = 0;
return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits<s32>::max(), page_table, process_address, size, device_address, device_perm, refresh_mappings);
return this->MapImpl(out_mapped_size, num_pt, refresh_mappings ? 1 : std::numeric_limits<s32>::max(), pg, device_address, device_perm);
}
Result KDevicePageTable::Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) {
Result KDevicePageTable::Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
/* Validate address/size. */
const size_t size = pg.GetNumPages() * PageSize;
MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0);
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
/* Ensure the page group is correct. */
R_UNLESS(this->Compare(page_table, process_address, size, device_address), svc::ResultInvalidCurrentMemory());
R_UNLESS(this->Compare(pg, device_address), svc::ResultInvalidCurrentMemory());
/* Unmap the pages. */
this->UnmapImpl(device_address, size, false);

View File

@@ -492,7 +492,7 @@ namespace ams::kern::board::nintendo::nx {
/* Wait for a request. */
{
KScopedLightLock lk(g_cv_lock);
while ((g_sleep_target_cores & target_core_mask) == 0) {
while (!(g_sleep_target_cores & target_core_mask)) {
g_cv.Wait(std::addressof(g_cv_lock));
}
}

View File

@@ -95,10 +95,9 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm:
mrs x2, tpidr_el1
stp x1, x2, [x0], #0x10
/* Save the virtual resumption entrypoint and cntv_cval_el0. */
/* Save the virtual resumption entrypoint. */
adr x1, 77f
mrs x2, cntv_cval_el0
stp x1, x2, [x0], #0x10
stp x1, xzr, [x0], #0x10
/* Get the current core id. */
mrs x0, mpidr_el1
@@ -246,13 +245,12 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm:
msr tcr_el1, x1
msr mair_el1, x2
/* Get sctlr, tpidr, the entrypoint, and cntv_cval_el0. */
ldp x1, x2, [x0], #0x10
ldp x3, x4, [x0], #0x10
/* Get sctlr, tpidr, and the entrypoint. */
ldp x1, x2, [x0], #0x10
ldp x3, xzr, [x0], #0x10
/* Set the global context back into x18/tpidr. */
msr tpidr_el1, x2
msr cntv_cval_el0, x4
dsb sy
isb

View File

@@ -21,8 +21,7 @@ namespace ams::kern::board::nintendo::nx {
namespace {
constexpr uintptr_t DramPhysicalAddress = 0x80000000;
constexpr size_t SecureAlignment = 128_KB;
constexpr size_t SecureAlignment = 128_KB;
/* Global variables for panic. */
constinit bool g_call_smc_on_panic;
@@ -42,8 +41,8 @@ namespace ams::kern::board::nintendo::nx {
/* Nintendo uses std::mt19937_t for randomness. */
/* To save space (and because mt19337_t isn't secure anyway), */
/* We will use TinyMT. */
constinit bool g_initialized_random_generator;
constinit util::TinyMT g_random_generator;
bool g_initialized_random_generator;
util::TinyMT g_random_generator;
constinit KSpinLock g_random_lock;
ALWAYS_INLINE size_t GetRealMemorySizeForInit() {
@@ -90,10 +89,13 @@ namespace ams::kern::board::nintendo::nx {
return value;
}
ALWAYS_INLINE u64 GenerateRandomU64FromSmc() {
u64 value;
smc::GenerateRandomBytes(std::addressof(value), sizeof(value));
return value;
void EnsureRandomGeneratorInitialized() {
if (AMS_UNLIKELY(!g_initialized_random_generator)) {
u64 seed;
smc::GenerateRandomBytes(&seed, sizeof(seed));
g_random_generator.Initialize(reinterpret_cast<u32*>(&seed), sizeof(seed) / sizeof(u32));
g_initialized_random_generator = true;
}
}
ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() {
@@ -312,12 +314,12 @@ namespace ams::kern::board::nintendo::nx {
g_secure_applet_memory_used = false;
}
u32 GetVersionIdentifier() {
u32 value = 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 8;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 16;
value |= static_cast<u64>('M') << 24;
u64 GetVersionIdentifier() {
u64 value = kern::GetTargetFirmware();
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 32;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 40;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 48;
value |= static_cast<u64>('M') << 56;
return value;
}
@@ -346,10 +348,6 @@ namespace ams::kern::board::nintendo::nx {
}
}
KPhysicalAddress KSystemControl::Init::GetInitialProcessBinaryPhysicalAddress() {
return GetKernelPhysicalBaseAddress(DramPhysicalAddress) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax;
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
return GetKernelConfigurationForInit().Get<smc::KernelConfiguration::IncreaseThreadResourceLimit>();
}
@@ -370,7 +368,7 @@ namespace ams::kern::board::nintendo::nx {
case smc::MemoryArrangement_6GBForAppletDev:
return 3285_MB;
case smc::MemoryArrangement_8GB:
return 6964_MB;
return 4916_MB;
}
}();
@@ -394,12 +392,12 @@ namespace ams::kern::board::nintendo::nx {
case smc::MemoryArrangement_6GBForAppletDev:
return 2193_MB;
case smc::MemoryArrangement_8GB:
return 562_MB;
return 2193_MB;
}
}();
/* Return (possibly) adjusted size. */
constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB;
constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
@@ -436,14 +434,6 @@ namespace ams::kern::board::nintendo::nx {
/* System Initialization. */
void KSystemControl::InitializePhase1() {
/* Initialize our random generator. */
{
u64 seed;
smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed));
g_random_generator.Initialize(reinterpret_cast<u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
g_initialized_random_generator = true;
}
/* Set IsDebugMode. */
{
KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode));
@@ -549,23 +539,18 @@ namespace ams::kern::board::nintendo::nx {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
EnsureRandomGeneratorInitialized();
if (AMS_LIKELY(g_initialized_random_generator)) {
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
} else {
return GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
}
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
}
u64 KSystemControl::GenerateRandomU64() {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
if (AMS_LIKELY(g_initialized_random_generator)) {
return GenerateRandomU64FromGenerator();
} else {
return GenerateRandomU64FromSmc();
}
EnsureRandomGeneratorInitialized();
return GenerateRandomU64FromGenerator();
}
void KSystemControl::SleepSystem() {
@@ -599,8 +584,8 @@ namespace ams::kern::board::nintendo::nx {
f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress();
/* Set afsr1. */
f_ctx->afsr0 = GetVersionIdentifier();
f_ctx->afsr1 = static_cast<u32>(kern::GetTargetFirmware());
f_ctx->afsr0 = 0;
f_ctx->afsr1 = GetVersionIdentifier();
/* Set efsr/far. */
f_ctx->far = cpu::GetFarEl1();

View File

@@ -22,6 +22,7 @@ namespace ams::kern::init {
#define FOREACH_SLAB_TYPE(HANDLER, ...) \
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \
HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
HANDLER(KLinkedListNode, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \
HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
@@ -57,14 +58,14 @@ namespace ams::kern::init {
/* Constexpr counts. */
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKEvent = 700;
constexpr size_t SlabCountKInterruptEvent = 100;
constexpr size_t SlabCountKPort = 256 + 0x20 /* Extra 0x20 ports over Nintendo for homebrew. */;
constexpr size_t SlabCountKPort = 256;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKSession = 933;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
@@ -76,13 +77,13 @@ namespace ams::kern::init {
namespace test {
constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo));
constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + sizeof(KLinkedListNode) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo));
static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize);
}
/* Global to hold our resource counts. */
constinit KSlabResourceCounts g_slab_resource_counts = {
KSlabResourceCounts g_slab_resource_counts = {
.num_KProcess = SlabCountKProcess,
.num_KThread = SlabCountKThread,
.num_KEvent = SlabCountKEvent,
@@ -131,9 +132,7 @@ namespace ams::kern::init {
}
size_t CalculateSlabHeapGapSize() {
constexpr size_t KernelSlabHeapGapSize = 2_MB - 296_KB;
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
return KernelSlabHeapGapSize;
return (kern::GetTargetFirmware() >= TargetFirmware_10_0_0) ? KernelSlabHeapGapsSize : KernelSlabHeapGapsSizeDeprecated;
}
size_t CalculateTotalSlabHeapSize() {

View File

@@ -25,219 +25,101 @@ namespace ams::kern {
s32 priority;
};
constinit KVirtualAddress g_initial_process_binary_address = Null<KVirtualAddress>;
constinit InitialProcessBinaryHeader g_initial_process_binary_header = {};
constinit size_t g_initial_process_secure_memory_size = 0;
constinit u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
constinit u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
void LoadInitialProcessBinaryHeader() {
if (g_initial_process_binary_header.magic != InitialProcessBinaryMagic) {
/* Get the virtual address for the image. */
const KVirtualAddress virt_addr = GetInitialProcessBinaryAddress();
/* Copy and validate the header. */
g_initial_process_binary_header = *GetPointer<InitialProcessBinaryHeader>(virt_addr);
MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.magic == InitialProcessBinaryMagic);
MESOSPHERE_ABORT_UNLESS(g_initial_process_binary_header.num_processes <= init::GetSlabResourceCounts().num_KProcess);
/* Set the image address. */
g_initial_process_binary_address = virt_addr;
/* Process/calculate the secure memory size. */
KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader);
const KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size;
const size_t num_processes = g_initial_process_binary_header.num_processes;
for (size_t i = 0; i < num_processes; ++i) {
/* Validate that we can read the current KIP. */
MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader));
/* Attach to the current KIP. */
KInitialProcessReader reader;
MESOSPHERE_ABORT_UNLESS(reader.Attach(current) != Null<KVirtualAddress>);
/* If the process uses secure memory, account for that. */
if (reader.UsesSecureMemory()) {
g_initial_process_secure_memory_size += reader.GetSize() + util::AlignUp(reader.GetStackSize(), PageSize);
}
}
}
KVirtualAddress GetInitialProcessBinaryAddress() {
const uintptr_t end_address = KMemoryLayout::GetPageTableHeapRegion().GetEndAddress();
MESOSPHERE_ABORT_UNLESS(end_address != 0);
return end_address - InitialProcessBinarySizeMax;
}
void CreateProcesses(InitialProcessInfo *infos) {
/* Determine process image extents. */
KVirtualAddress current = g_initial_process_binary_address + sizeof(InitialProcessBinaryHeader);
KVirtualAddress end = g_initial_process_binary_address + g_initial_process_binary_header.size;
void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) {
if (header->magic != InitialProcessBinaryMagic) {
*header = *GetPointer<InitialProcessBinaryHeader>(GetInitialProcessBinaryAddress());
}
MESOSPHERE_ABORT_UNLESS(header->magic == InitialProcessBinaryMagic);
MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess);
}
size_t GetProcessesSecureMemorySize(KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) {
u8 *current = GetPointer<u8>(binary_address + sizeof(InitialProcessBinaryHeader));
const u8 * const end = GetPointer<u8>(binary_address + header.size - sizeof(KInitialProcessHeader));
size_t size = 0;
const size_t num_processes = header.num_processes;
for (size_t i = 0; i < num_processes; i++) {
/* Validate that we can read the current KIP. */
MESOSPHERE_ABORT_UNLESS(current <= end);
KInitialProcessReader reader;
MESOSPHERE_ABORT_UNLESS(reader.Attach(current));
/* If the process uses secure memory, account for that. */
if (reader.UsesSecureMemory()) {
size += util::AlignUp(reader.GetSize(), PageSize);
}
/* Advance the reader. */
current += reader.GetBinarySize();
}
return size;
}
void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) {
u8 *current = GetPointer<u8>(binary_address + sizeof(InitialProcessBinaryHeader));
const u8 * const end = GetPointer<u8>(binary_address + header.size - sizeof(KInitialProcessHeader));
/* Decide on pools to use. */
const auto unsafe_pool = static_cast<KMemoryManager::Pool>(KSystemControl::GetCreateProcessMemoryPool());
const auto secure_pool = (GetTargetFirmware() >= TargetFirmware_2_0_0) ? KMemoryManager::Pool_Secure : unsafe_pool;
const size_t num_processes = g_initial_process_binary_header.num_processes;
for (size_t i = 0; i < num_processes; ++i) {
/* Validate that we can read the current KIP header. */
MESOSPHERE_ABORT_UNLESS(current <= end - sizeof(KInitialProcessHeader));
/* Attach to the current kip. */
const size_t num_processes = header.num_processes;
for (size_t i = 0; i < num_processes; i++) {
/* Validate that we can read the current KIP. */
MESOSPHERE_ABORT_UNLESS(current <= end);
KInitialProcessReader reader;
KVirtualAddress data = reader.Attach(current);
MESOSPHERE_ABORT_UNLESS(data != Null<KVirtualAddress>);
MESOSPHERE_ABORT_UNLESS(reader.Attach(current));
/* Ensure that the remainder of our parse is page aligned. */
if (!util::IsAligned(GetInteger(data), PageSize)) {
const KVirtualAddress aligned_data = util::AlignDown(GetInteger(data), PageSize);
std::memmove(GetVoidPointer(aligned_data), GetVoidPointer(data), end - data);
data = aligned_data;
end -= (data - aligned_data);
}
/* If we crossed a page boundary, free the pages we're done using. */
if (KVirtualAddress aligned_current = util::AlignDown(GetInteger(current), PageSize); aligned_current != data) {
const size_t freed_size = data - aligned_current;
Kernel::GetMemoryManager().Close(aligned_current, freed_size / PageSize);
Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, freed_size);
}
/* Parse process parameters. */
/* Parse process parameters and reserve memory. */
ams::svc::CreateProcessParameter params;
MESOSPHERE_R_ABORT_UNLESS(reader.MakeCreateProcessParameter(std::addressof(params), true));
/* Get the binary size for the kip. */
const size_t binary_size = reader.GetBinarySize();
const size_t binary_pages = binary_size / PageSize;
/* Get the pool for both the current (compressed) image, and the decompressed process. */
const auto src_pool = Kernel::GetMemoryManager().GetPool(data);
const auto dst_pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool;
/* Determine the process size, and how much memory isn't already reserved. */
const size_t process_size = params.code_num_pages * PageSize;
const size_t unreserved_size = process_size - (src_pool == dst_pool ? util::AlignDown(binary_size, PageSize) : 0);
/* Reserve however much memory we need to reserve. */
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, unreserved_size));
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, params.code_num_pages * PageSize));
/* Create the process. */
KProcess *new_process = nullptr;
{
/* Make page groups to represent the data. */
/* Declare page group to use for process memory. */
KPageGroup pg(std::addressof(Kernel::GetBlockInfoManager()));
KPageGroup workaround_pg(std::addressof(Kernel::GetBlockInfoManager()));
/* Populate the page group to represent the data. */
/* Allocate memory for the process. */
auto &mm = Kernel::GetMemoryManager();
const auto pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool;
MESOSPHERE_R_ABORT_UNLESS(mm.AllocateAndOpen(std::addressof(pg), params.code_num_pages, KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront)));
{
/* Allocate the previously unreserved pages. */
KPageGroup unreserve_pg(std::addressof(Kernel::GetBlockInfoManager()));
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(unreserve_pg), unreserved_size / PageSize, KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront)));
/* Ensure that we do not leak pages. */
ON_SCOPE_EXIT { pg.Close(); };
/* Add the previously reserved pages. */
if (src_pool == dst_pool && binary_pages != 0) {
/* NOTE: Nintendo does not check the result of this operation. */
pg.AddBlock(data, binary_pages);
}
/* Get the temporary region. */
const auto &temp_region = KMemoryLayout::GetTempRegion();
MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0);
/* Add the previously unreserved pages. */
for (const auto &block : unreserve_pg) {
/* NOTE: Nintendo does not check the result of this operation. */
pg.AddBlock(block.GetAddress(), block.GetNumPages());
}
/* Map the process's memory into the temporary region. */
KProcessAddress temp_address = Null<KProcessAddress>;
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite));
/* Load the process. */
MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params));
/* Unmap the temporary mapping. */
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel));
/* Create a KProcess object. */
new_process = KProcess::Create();
MESOSPHERE_ABORT_UNLESS(new_process != nullptr);
/* Initialize the process. */
MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool));
}
MESOSPHERE_ABORT_UNLESS(pg.GetNumPages() == static_cast<size_t>(params.code_num_pages));
/* Ensure that we do not leak pages. */
KPageGroup *process_pg = std::addressof(pg);
ON_SCOPE_EXIT { process_pg->Close(); };
/* Get the temporary region. */
const auto &temp_region = KMemoryLayout::GetTempRegion();
MESOSPHERE_ABORT_UNLESS(temp_region.GetEndAddress() != 0);
/* Map the process's memory into the temporary region. */
KProcessAddress temp_address = Null<KProcessAddress>;
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite));
/* Setup the new page group's memory, so that we can load the process. */
{
/* Copy the unaligned ending of the compressed binary. */
if (const size_t unaligned_size = binary_size - util::AlignDown(binary_size, PageSize); unaligned_size != 0) {
std::memcpy(GetVoidPointer(temp_address + process_size - unaligned_size), GetVoidPointer(data + binary_size - unaligned_size), unaligned_size);
}
/* Copy the aligned part of the compressed binary. */
if (const size_t aligned_size = util::AlignDown(binary_size, PageSize); aligned_size != 0 && src_pool == dst_pool) {
std::memmove(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(temp_address), aligned_size);
} else {
if (src_pool != dst_pool) {
std::memcpy(GetVoidPointer(temp_address + process_size - binary_size), GetVoidPointer(data), aligned_size);
Kernel::GetMemoryManager().Close(data, aligned_size / PageSize);
}
}
/* Clear the first part of the memory. */
std::memset(GetVoidPointer(temp_address), 0, process_size - binary_size);
}
/* Load the process. */
MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params, temp_address + process_size - binary_size));
/* Unmap the temporary mapping. */
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel));
/* Create a KProcess object. */
new_process = KProcess::Create();
MESOSPHERE_ABORT_UNLESS(new_process != nullptr);
/* Ensure the page group is usable for the process. */
/* If the pool is the same, we need to use the workaround page group. */
if (src_pool == dst_pool) {
/* Allocate a new, usable group for the process. */
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(workaround_pg), static_cast<size_t>(params.code_num_pages), KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront)));
/* Copy data from the working page group to the usable one. */
auto work_it = pg.begin();
MESOSPHERE_ABORT_UNLESS(work_it != pg.end());
{
auto work_address = work_it->GetAddress();
auto work_remaining = work_it->GetNumPages();
for (const auto &block : workaround_pg) {
auto block_address = block.GetAddress();
auto block_remaining = block.GetNumPages();
while (block_remaining > 0) {
if (work_remaining == 0) {
++work_it;
work_address = work_it->GetAddress();
work_remaining = work_it->GetNumPages();
}
const size_t cur_pages = std::min(block_remaining, work_remaining);
const size_t cur_size = cur_pages * PageSize;
std::memcpy(GetVoidPointer(block_address), GetVoidPointer(work_address), cur_size);
block_address += cur_size;
work_address += cur_size;
block_remaining -= cur_pages;
work_remaining -= cur_pages;
}
}
++work_it;
}
MESOSPHERE_ABORT_UNLESS(work_it == pg.end());
/* We want to use the new page group. */
process_pg = std::addressof(workaround_pg);
pg.Close();
}
/* Initialize the process. */
MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, *process_pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), dst_pool, reader.IsImmortal()));
}
/* Release the memory that was previously reserved. */
if (const size_t aligned_bin_size = util::AlignDown(binary_size, PageSize); aligned_bin_size != 0 && src_pool != dst_pool) {
Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, aligned_bin_size);
}
/* Set the process's memory permissions. */
@@ -255,18 +137,15 @@ namespace ams::kern {
infos[i].priority = reader.GetPriority();
/* Advance the reader. */
current = data + binary_size;
}
/* Release remaining memory used by the image. */
{
const size_t remaining_size = util::AlignUp(GetInteger(g_initial_process_binary_address) + g_initial_process_binary_header.size, PageSize) - util::AlignDown(GetInteger(current), PageSize);
const size_t remaining_pages = remaining_size / PageSize;
Kernel::GetMemoryManager().Close(util::AlignDown(GetInteger(current), PageSize), remaining_pages);
Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, remaining_size);
current += reader.GetBinarySize();
}
}
constinit KVirtualAddress g_initial_process_binary_address = Null<KVirtualAddress>;
constinit InitialProcessBinaryHeader g_initial_process_binary_header = {};
constinit u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
constinit u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
}
u64 GetInitialProcessIdMin() {
@@ -277,37 +156,32 @@ namespace ams::kern {
return g_initial_process_id_max;
}
KVirtualAddress GetInitialProcessBinaryAddress() {
/* Get, validate the pool region. */
const auto *pool_region = KMemoryLayout::GetVirtualMemoryRegionTree().FindLastDerived(KMemoryRegionType_VirtualDramUserPool);
MESOSPHERE_INIT_ABORT_UNLESS(pool_region != nullptr);
MESOSPHERE_INIT_ABORT_UNLESS(pool_region->GetEndAddress() != 0);
MESOSPHERE_ABORT_UNLESS(pool_region->GetSize() >= InitialProcessBinarySizeMax);
return pool_region->GetEndAddress() - InitialProcessBinarySizeMax;
}
size_t GetInitialProcessesSecureMemorySize() {
LoadInitialProcessBinaryHeader();
LoadInitialProcessBinaryHeader(&g_initial_process_binary_header);
return g_initial_process_secure_memory_size;
return GetProcessesSecureMemorySize(g_initial_process_binary_address != Null<KVirtualAddress> ? g_initial_process_binary_address : GetInitialProcessBinaryAddress(), g_initial_process_binary_header);
}
size_t CopyInitialProcessBinaryToKernelMemory() {
LoadInitialProcessBinaryHeader();
void CopyInitialProcessBinaryToKernelMemory() {
LoadInitialProcessBinaryHeader(&g_initial_process_binary_header);
if (g_initial_process_binary_header.num_processes > 0) {
/* Reserve pages for the initial process binary from the system resource limit. */
auto &mm = Kernel::GetMemoryManager();
const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize);
const size_t num_pages = total_size / PageSize;
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size));
/* The initial process binary is potentially over-allocated, so free any extra pages. */
if (total_size < InitialProcessBinarySizeMax) {
Kernel::GetMemoryManager().Close(g_initial_process_binary_address + total_size, (InitialProcessBinarySizeMax - total_size) / PageSize);
}
/* Allocate memory for the image. */
const KMemoryManager::Pool pool = static_cast<KMemoryManager::Pool>(KSystemControl::GetCreateProcessMemoryPool());
const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront);
KVirtualAddress allocated_memory = mm.AllocateAndOpenContinuous(num_pages, 1, allocate_option);
MESOSPHERE_ABORT_UNLESS(allocated_memory != Null<KVirtualAddress>);
return total_size;
} else {
return 0;
/* Relocate the image. */
std::memmove(GetVoidPointer(allocated_memory), GetVoidPointer(GetInitialProcessBinaryAddress()), g_initial_process_binary_header.size);
std::memset(GetVoidPointer(GetInitialProcessBinaryAddress()), 0, g_initial_process_binary_header.size);
g_initial_process_binary_address = allocated_memory;
}
}
@@ -316,7 +190,15 @@ namespace ams::kern {
InitialProcessInfo *infos = static_cast<InitialProcessInfo *>(__builtin_alloca(sizeof(InitialProcessInfo) * g_initial_process_binary_header.num_processes));
/* Create the processes. */
CreateProcesses(infos);
CreateProcesses(infos, g_initial_process_binary_address, g_initial_process_binary_header);
/* Release the memory used by the image. */
{
const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize);
const size_t num_pages = total_size / PageSize;
Kernel::GetMemoryManager().Close(g_initial_process_binary_address, num_pages);
Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, total_size);
}
/* Determine the initial process id range. */
for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) {
@@ -328,7 +210,6 @@ namespace ams::kern {
/* Run the processes. */
for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) {
MESOSPHERE_R_ABORT_UNLESS(infos[i].process->Run(infos[i].priority, infos[i].stack_size));
infos[i].process->Close();
}
}

View File

@@ -22,7 +22,7 @@ namespace ams::kern {
/* Most fields have already been cleared by our constructor. */
/* Initial processes may run on all cores. */
m_core_mask = cpu::VirtualCoreMask;
m_core_mask = (1ul << cpu::NumCores) - 1;
/* Initial processes may use any user priority they like. */
m_priority_mask = ~0xFul;
@@ -55,17 +55,18 @@ namespace ams::kern {
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
/* Set core mask. */
for (auto core_id = min_core; core_id <= max_core; core_id++) {
m_core_mask |= (1ul << core_id);
}
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
/* Set priority mask. */
for (auto prio = min_prio; prio <= max_prio; prio++) {

View File

@@ -42,10 +42,6 @@ namespace ams::kern {
return this->GetParent()->IsLight();
}
bool KClientPort::IsServerClosed() const {
return this->GetParent()->IsServerClosed();
}
void KClientPort::Destroy() {
/* Note with our parent that we're closed. */
m_parent->OnClientClosed();

View File

@@ -23,11 +23,12 @@ namespace ams::kern {
/* Set members. */
m_owner = GetCurrentProcessPointer();
/* Get the owner page table. */
/* Initialize the page group. */
auto &page_table = m_owner->GetPageTable();
new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager());
/* Construct the page group, guarding to make sure our state is valid on exit. */
auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager());
/* Ensure that our page group's state is valid on exit. */
auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); };
/* Lock the memory. */
R_TRY(page_table.LockForCodeMemory(GetPointer(m_page_group), addr, size));

View File

@@ -102,13 +102,14 @@ namespace ams::kern {
}
}
MESOSPHERE_ASSERT(owner_thread.IsNotNull());
}
/* Remove the thread as a waiter from the lock owner. */
{
KScopedSchedulerLock sl;
if (KThread *mutex_owner = cur_thread->GetLockOwner(); mutex_owner != nullptr) {
mutex_owner->RemoveWaiter(cur_thread);
}
/* Remove the thread as a waiter from the lock owner. */
{
KScopedSchedulerLock sl;
KThread *owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
@@ -117,7 +118,7 @@ namespace ams::kern {
return cur_thread->GetWaitResult(std::addressof(dummy));
}
void KConditionVariable::SignalImpl(KThread *thread) {
KThread *KConditionVariable::SignalImpl(KThread *thread) {
/* Check pre-conditions. */
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
@@ -136,6 +137,7 @@ namespace ams::kern {
}
}
KThread *thread_to_close = nullptr;
if (AMS_LIKELY(can_access)) {
if (prev_tag == ams::svc::InvalidHandle) {
/* If nobody held the lock previously, we're all good. */
@@ -148,7 +150,7 @@ namespace ams::kern {
if (AMS_LIKELY(owner_thread != nullptr)) {
/* Add the thread as a waiter on the owner. */
owner_thread->AddWaiter(thread);
owner_thread->Close();
thread_to_close = owner_thread;
} else {
/* The lock was tagged with a thread that doesn't exist. */
thread->SetSyncedObject(nullptr, svc::ResultInvalidState());
@@ -160,9 +162,17 @@ namespace ams::kern {
thread->SetSyncedObject(nullptr, svc::ResultInvalidCurrentMemory());
thread->Wakeup();
}
return thread_to_close;
}
void KConditionVariable::Signal(uintptr_t cv_key, s32 count) {
/* Prepare for signaling. */
constexpr int MaxThreads = 16;
KLinkedList<KThread> thread_list;
KThread *thread_array[MaxThreads];
int num_to_close = 0;
/* Perform signaling. */
int num_waiters = 0;
{
@@ -172,7 +182,14 @@ namespace ams::kern {
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) {
KThread *target_thread = std::addressof(*it);
this->SignalImpl(target_thread);
if (KThread *thread = this->SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
thread_list.push_back(*thread);
}
}
it = m_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
@@ -184,6 +201,16 @@ namespace ams::kern {
WriteToUser(cv_key, std::addressof(has_waiter_flag));
}
}
/* Close threads in the array. */
for (auto i = 0; i < num_to_close; ++i) {
thread_array[i]->Close();
}
/* Close threads in the list. */
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
(*it).Close();
}
}
Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) {
@@ -249,6 +276,11 @@ namespace ams::kern {
}
}
/* Cancel the timer wait. */
if (timer != nullptr) {
timer->CancelTask(cur_thread);
}
/* Remove from the condition variable. */
{
KScopedSchedulerLock sl;
@@ -263,11 +295,6 @@ namespace ams::kern {
}
}
/* Cancel the timer wait. */
if (timer != nullptr) {
timer->CancelTask(cur_thread);
}
/* Get the result. */
KSynchronizationObject *dummy;
return cur_thread->GetWaitResult(std::addressof(dummy));

View File

@@ -92,7 +92,51 @@ namespace ams::kern {
R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size));
} else {
/* The memory is IO memory. */
R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size));
/* Verify that the memory is readable. */
R_UNLESS((info.GetPermission() & KMemoryPermission_UserRead) == KMemoryPermission_UserRead, svc::ResultInvalidAddress());
/* Get the physical address of the memory. */
/* NOTE: Nintendo does not verify the result of this call. */
KPhysicalAddress phys_addr;
target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address);
/* Map the address as IO in the current process. */
R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserRead));
/* Get the address of the newly mapped IO. */
KProcessAddress io_address;
Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize);
MESOSPHERE_R_ASSERT(query_result);
R_TRY(query_result);
/* Ensure we clean up the new mapping on scope exit. */
ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); };
/* Adjust the io address for alignment. */
io_address += (GetInteger(cur_address) & (PageSize - 1));
/* Get the readable size. */
const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address));
/* Read the memory. */
switch ((GetInteger(cur_address) | readable_size) & 3) {
case 0:
{
R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
}
break;
case 2:
{
R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
}
break;
default:
{
R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer());
}
break;
}
}
/* Advance. */
@@ -141,7 +185,51 @@ namespace ams::kern {
R_TRY(target_pt.WriteDebugMemory(cur_address, GetVoidPointer(buffer), cur_size));
} else {
/* The memory is IO memory. */
R_TRY(target_pt.WriteDebugIoMemory(cur_address, GetVoidPointer(buffer), cur_size));
/* Verify that the memory is writable. */
R_UNLESS((info.GetPermission() & KMemoryPermission_UserReadWrite) == KMemoryPermission_UserReadWrite, svc::ResultInvalidAddress());
/* Get the physical address of the memory. */
/* NOTE: Nintendo does not verify the result of this call. */
KPhysicalAddress phys_addr;
target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address);
/* Map the address as IO in the current process. */
R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserReadWrite));
/* Get the address of the newly mapped IO. */
KProcessAddress io_address;
Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize);
MESOSPHERE_R_ASSERT(query_result);
R_TRY(query_result);
/* Ensure we clean up the new mapping on scope exit. */
ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); };
/* Adjust the io address for alignment. */
io_address += (GetInteger(cur_address) & (PageSize - 1));
/* Get the readable size. */
const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address));
/* Read the memory. */
switch ((GetInteger(cur_address) | readable_size) & 3) {
case 0:
{
R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
}
break;
case 2:
{
R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
}
break;
default:
{
R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer());
}
break;
}
}
/* Advance. */
@@ -355,15 +443,6 @@ namespace ams::kern {
} else if (state == KProcess::State_DebugBreak) {
/* If the process is debug breaked, transition it accordingly. */
new_state = KProcess::State_Crashed;
/* Suspend all the threads in the process. */
{
auto end = target->GetThreadList().end();
for (auto it = target->GetThreadList().begin(); it != end; ++it) {
/* Request that we suspend the thread. */
it->RequestSuspend(KThread::SuspendType_Debug);
}
}
} else {
/* Otherwise, don't transition. */
new_state = state;
@@ -478,12 +557,8 @@ namespace ams::kern {
/* Verify that the thread's svc state is valid. */
if (thread->IsCallingSvc()) {
const u8 svc_id = thread->GetSvcId();
const bool is_valid_svc = svc_id == svc::SvcId_Break ||
svc_id == svc::SvcId_ReturnFromException;
R_UNLESS(is_valid_svc, svc::ResultInvalidState());
R_UNLESS(thread->GetSvcId() != svc::SvcId_Break, svc::ResultInvalidState());
R_UNLESS(thread->GetSvcId() != svc::SvcId_ReturnFromException, svc::ResultInvalidState());
}
/* Set the thread context. */
@@ -849,6 +924,9 @@ namespace ams::kern {
/* If the process isn't null, detach. */
if (process.IsNotNull()) {
/* When we're done detaching, clear the reference we opened when we attached. */
ON_SCOPE_EXIT { process->Close(); };
/* Detach. */
{
/* Lock both ourselves and the target process. */
@@ -883,9 +961,6 @@ namespace ams::kern {
/* Clear our process. */
m_process = nullptr;
}
/* We're done detaching, so clear the reference we opened when we attached. */
process->Close();
}
}
}

View File

@@ -71,11 +71,12 @@ namespace ams::kern {
/* Lock the address space. */
KScopedLightLock lk(m_lock);
/* Lock the page table to prevent concurrent device mapping operations. */
KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
/* Lock the pages. */
R_TRY(page_table->LockForMapDeviceAddressSpace(process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned));
KPageGroup pg(page_table->GetBlockInfoManager());
R_TRY(page_table->LockForDeviceAddressSpace(std::addressof(pg), process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned));
/* Close the pages we opened when we're done with them. */
ON_SCOPE_EXIT { pg.Close(); };
/* Ensure that if we fail, we don't keep unmapped pages locked. */
auto unlock_guard = SCOPE_GUARD { MESOSPHERE_R_ABORT_UNLESS(page_table->UnlockForDeviceAddressSpace(process_address, size)); };
@@ -86,7 +87,7 @@ namespace ams::kern {
auto mapped_size_guard = SCOPE_GUARD { *out_mapped_size = 0; };
/* Perform the mapping. */
R_TRY(m_table.Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, refresh_mappings));
R_TRY(m_table.Map(out_mapped_size, pg, device_address, device_perm, refresh_mappings));
/* Ensure that we unmap the pages if we fail to update the protections. */
/* NOTE: Nintendo does not check the result of this unmap call. */
@@ -112,18 +113,19 @@ namespace ams::kern {
/* Lock the address space. */
KScopedLightLock lk(m_lock);
/* Lock the page table to prevent concurrent device mapping operations. */
KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
/* Make and open a page group for the unmapped region. */
KPageGroup pg(page_table->GetBlockInfoManager());
R_TRY(page_table->MakePageGroupForUnmapDeviceAddressSpace(std::addressof(pg), process_address, size));
/* Lock the pages. */
R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size));
/* Ensure the page group is closed on scope exit. */
ON_SCOPE_EXIT { pg.Close(); };
/* If we fail to unmap, we want to do a partial unlock. */
{
auto unlock_guard = SCOPE_GUARD { page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size, size); };
/* Unmap. */
R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
R_TRY(m_table.Unmap(pg, device_address));
unlock_guard.Cancel();
}

View File

@@ -174,20 +174,13 @@ namespace ams::kern {
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread());
/* Get reference to the current thread. */
KThread &cur_thread = GetCurrentThread();
/* Enable interrupts, temporarily. */
KScopedInterruptEnable ei;
/* If the thread is scheduled for termination, exit the thread. */
if (cur_thread.IsTerminationRequested()) {
cur_thread.Exit();
__builtin_unreachable();
/* The only deferred procedure supported by Horizon is thread termination. */
/* Check if we need to terminate the current thread. */
KThread *cur_thread = GetCurrentThreadPointer();
if (cur_thread->IsTerminationRequested()) {
KScopedInterruptEnable ei;
cur_thread->Exit();
}
/* We may also need to destroy any closed objects. */
cur_thread.DestroyClosedObjects();
}
void KDpcManager::Sync() {

View File

@@ -345,6 +345,7 @@ namespace ams::kern::KDumpObject {
DUMP_KSLABOBJ(KDebug);
DUMP_KSLABOBJ(KSession);
DUMP_KSLABOBJ(KLightSession);
DUMP_KSLABOBJ(KLinkedListNode);
DUMP_KSLABOBJ(KThreadLocalPage);
DUMP_KSLABOBJ(KObjectName);
DUMP_KSLABOBJ(KEventInfo);

View File

@@ -21,18 +21,23 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS();
/* Get the table and clear our record of it. */
Entry *saved_table = nullptr;
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd;
KScopedSpinLock lk(m_lock);
std::swap(m_table, saved_table);
std::swap(m_table_size, saved_table_size);
}
/* Close and free all entries. */
for (size_t i = 0; i < saved_table_size; i++) {
if (KAutoObject *obj = m_objects[i]; obj != nullptr) {
Entry *entry = std::addressof(saved_table[i]);
if (KAutoObject *obj = entry->GetObject(); obj != nullptr) {
obj->Close();
this->FreeEntry(entry);
}
}
@@ -43,13 +48,12 @@ namespace ams::kern {
MESOSPHERE_ASSERT_THIS();
/* Don't allow removal of a pseudo-handle. */
if (AMS_UNLIKELY(ams::svc::IsPseudoHandle(handle))) {
if (ams::svc::IsPseudoHandle(handle)) {
return false;
}
/* Handles must not have reserved bits set. */
const auto handle_pack = GetHandleBitPack(handle);
if (AMS_UNLIKELY(handle_pack.Get<HandleReserved>() != 0)) {
if (GetHandleBitPack(handle).Get<HandleReserved>() != 0) {
return false;
}
@@ -59,11 +63,9 @@ namespace ams::kern {
KScopedDisableDispatch dd;
KScopedSpinLock lk(m_lock);
if (AMS_LIKELY(this->IsValidHandle(handle))) {
const auto index = handle_pack.Get<HandleIndex>();
obj = m_objects[index];
this->FreeEntry(index);
if (Entry *entry = this->FindEntry(handle); entry != nullptr) {
obj = entry->GetObject();
this->FreeEntry(entry);
} else {
return false;
}
@@ -85,14 +87,10 @@ namespace ams::kern {
/* Allocate entry, set output handle. */
{
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
m_entry_infos[index].info = { .linear_id = linear_id, .type = type };
m_objects[index] = obj;
Entry *entry = this->AllocateEntry();
entry->SetUsed(obj, linear_id, type);
obj->Open();
*out_handle = EncodeHandle(index, linear_id);
*out_handle = EncodeHandle(this->GetEntryIndex(entry), linear_id);
}
return ResultSuccess();
@@ -106,7 +104,7 @@ namespace ams::kern {
/* Never exceed our capacity. */
R_UNLESS(m_count < m_table_size, svc::ResultOutOfHandles());
*out_handle = EncodeHandle(this->AllocateEntry(), this->AllocateLinearId());
*out_handle = EncodeHandle(this->GetEntryIndex(this->AllocateEntry()), this->AllocateLinearId());
return ResultSuccess();
}
@@ -122,13 +120,15 @@ namespace ams::kern {
const auto reserved = handle_pack.Get<HandleReserved>();
MESOSPHERE_ASSERT(reserved == 0);
MESOSPHERE_ASSERT(linear_id != 0);
MESOSPHERE_ASSERT(index < m_table_size);
MESOSPHERE_UNUSED(linear_id, reserved);
if (AMS_LIKELY(index < m_table_size)) {
/* NOTE: This code does not check the linear id. */
MESOSPHERE_ASSERT(m_objects[index] == nullptr);
this->FreeEntry(index);
}
/* Free the entry. */
/* NOTE: This code does not check the linear id. */
Entry *entry = std::addressof(m_table[index]);
MESOSPHERE_ASSERT(entry->GetObject() == nullptr);
this->FreeEntry(entry);
}
void KHandleTable::Register(ams::svc::Handle handle, KAutoObject *obj, u16 type) {
@@ -143,17 +143,15 @@ namespace ams::kern {
const auto reserved = handle_pack.Get<HandleReserved>();
MESOSPHERE_ASSERT(reserved == 0);
MESOSPHERE_ASSERT(linear_id != 0);
MESOSPHERE_ASSERT(index < m_table_size);
MESOSPHERE_UNUSED(reserved);
if (AMS_LIKELY(index < m_table_size)) {
/* Set the entry. */
MESOSPHERE_ASSERT(m_objects[index] == nullptr);
/* Set the entry. */
Entry *entry = std::addressof(m_table[index]);
MESOSPHERE_ASSERT(entry->GetObject() == nullptr);
m_entry_infos[index].info = { .linear_id = linear_id, .type = type };
m_objects[index] = obj;
obj->Open();
}
entry->SetUsed(obj, linear_id, type);
obj->Open();
}
}

View File

@@ -77,14 +77,14 @@ namespace ams::kern {
Result KInitialProcessReader::MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const {
/* Get and validate addresses/sizes. */
const uintptr_t rx_address = m_kip_header.GetRxAddress();
const size_t rx_size = m_kip_header.GetRxSize();
const uintptr_t ro_address = m_kip_header.GetRoAddress();
const size_t ro_size = m_kip_header.GetRoSize();
const uintptr_t rw_address = m_kip_header.GetRwAddress();
const size_t rw_size = m_kip_header.GetRwSize();
const uintptr_t bss_address = m_kip_header.GetBssAddress();
const size_t bss_size = m_kip_header.GetBssSize();
const uintptr_t rx_address = m_kip_header->GetRxAddress();
const size_t rx_size = m_kip_header->GetRxSize();
const uintptr_t ro_address = m_kip_header->GetRoAddress();
const size_t ro_size = m_kip_header->GetRoSize();
const uintptr_t rw_address = m_kip_header->GetRwAddress();
const size_t rw_size = m_kip_header->GetRwSize();
const uintptr_t bss_address = m_kip_header->GetBssAddress();
const size_t bss_size = m_kip_header->GetBssSize();
R_UNLESS(util::IsAligned(rx_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(ro_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(rw_address, PageSize), svc::ResultInvalidAddress());
@@ -115,13 +115,13 @@ namespace ams::kern {
/* Set fields in parameter. */
out->code_address = map_start + start_address;
out->code_num_pages = util::AlignUp(end_address - start_address, PageSize) / PageSize;
out->program_id = m_kip_header.GetProgramId();
out->version = m_kip_header.GetVersion();
out->program_id = m_kip_header->GetProgramId();
out->version = m_kip_header->GetVersion();
out->flags = 0;
MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize));
/* Copy name field. */
m_kip_header.GetName(out->name, sizeof(out->name));
m_kip_header->GetName(out->name, sizeof(out->name));
/* Apply ASLR, if needed. */
if (enable_aslr) {
@@ -146,36 +146,39 @@ namespace ams::kern {
return ResultSuccess();
}
Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter &params, KProcessAddress src) const {
Result KInitialProcessReader::Load(KProcessAddress address, const ams::svc::CreateProcessParameter &params) const {
/* Clear memory at the address. */
std::memset(GetVoidPointer(address), 0, params.code_num_pages * PageSize);
/* Prepare to layout the data. */
const KProcessAddress rx_address = address + m_kip_header.GetRxAddress();
const KProcessAddress ro_address = address + m_kip_header.GetRoAddress();
const KProcessAddress rw_address = address + m_kip_header.GetRwAddress();
const u8 *rx_binary = GetPointer<const u8>(src);
const u8 *ro_binary = rx_binary + m_kip_header.GetRxCompressedSize();
const u8 *rw_binary = ro_binary + m_kip_header.GetRoCompressedSize();
const KProcessAddress rx_address = address + m_kip_header->GetRxAddress();
const KProcessAddress ro_address = address + m_kip_header->GetRoAddress();
const KProcessAddress rw_address = address + m_kip_header->GetRwAddress();
const u8 *rx_binary = reinterpret_cast<const u8 *>(m_kip_header + 1);
const u8 *ro_binary = rx_binary + m_kip_header->GetRxCompressedSize();
const u8 *rw_binary = ro_binary + m_kip_header->GetRoCompressedSize();
/* Copy text. */
if (util::AlignUp(m_kip_header.GetRxSize(), PageSize)) {
std::memmove(GetVoidPointer(rx_address), rx_binary, m_kip_header.GetRxCompressedSize());
if (m_kip_header.IsRxCompressed()) {
BlzUncompress(GetVoidPointer(rx_address + m_kip_header.GetRxCompressedSize()));
if (util::AlignUp(m_kip_header->GetRxSize(), PageSize)) {
std::memcpy(GetVoidPointer(rx_address), rx_binary, m_kip_header->GetRxCompressedSize());
if (m_kip_header->IsRxCompressed()) {
BlzUncompress(GetVoidPointer(rx_address + m_kip_header->GetRxCompressedSize()));
}
}
/* Copy rodata. */
if (util::AlignUp(m_kip_header.GetRoSize(), PageSize)) {
std::memmove(GetVoidPointer(ro_address), ro_binary, m_kip_header.GetRoCompressedSize());
if (m_kip_header.IsRoCompressed()) {
BlzUncompress(GetVoidPointer(ro_address + m_kip_header.GetRoCompressedSize()));
if (util::AlignUp(m_kip_header->GetRoSize(), PageSize)) {
std::memcpy(GetVoidPointer(ro_address), ro_binary, m_kip_header->GetRoCompressedSize());
if (m_kip_header->IsRoCompressed()) {
BlzUncompress(GetVoidPointer(ro_address + m_kip_header->GetRoCompressedSize()));
}
}
/* Copy rwdata. */
if (util::AlignUp(m_kip_header.GetRwSize(), PageSize)) {
std::memmove(GetVoidPointer(rw_address), rw_binary, m_kip_header.GetRwCompressedSize());
if (m_kip_header.IsRwCompressed()) {
BlzUncompress(GetVoidPointer(rw_address + m_kip_header.GetRwCompressedSize()));
if (util::AlignUp(m_kip_header->GetRwSize(), PageSize)) {
std::memcpy(GetVoidPointer(rw_address), rw_binary, m_kip_header->GetRwCompressedSize());
if (m_kip_header->IsRwCompressed()) {
BlzUncompress(GetVoidPointer(rw_address + m_kip_header->GetRwCompressedSize()));
}
}
@@ -189,27 +192,27 @@ namespace ams::kern {
}
Result KInitialProcessReader::SetMemoryPermissions(KProcessPageTable &page_table, const ams::svc::CreateProcessParameter &params) const {
const size_t rx_size = m_kip_header.GetRxSize();
const size_t ro_size = m_kip_header.GetRoSize();
const size_t rw_size = m_kip_header.GetRwSize();
const size_t bss_size = m_kip_header.GetBssSize();
const size_t rx_size = m_kip_header->GetRxSize();
const size_t ro_size = m_kip_header->GetRoSize();
const size_t rw_size = m_kip_header->GetRwSize();
const size_t bss_size = m_kip_header->GetBssSize();
/* Set R-X pages. */
if (rx_size) {
const uintptr_t start = m_kip_header.GetRxAddress() + params.code_address;
const uintptr_t start = m_kip_header->GetRxAddress() + params.code_address;
R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(rx_size, PageSize), ams::svc::MemoryPermission_ReadExecute));
}
/* Set R-- pages. */
if (ro_size) {
const uintptr_t start = m_kip_header.GetRoAddress() + params.code_address;
const uintptr_t start = m_kip_header->GetRoAddress() + params.code_address;
R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(ro_size, PageSize), ams::svc::MemoryPermission_Read));
}
/* Set RW- pages. */
if (rw_size || bss_size) {
const uintptr_t start = (rw_size ? m_kip_header.GetRwAddress() : m_kip_header.GetBssAddress()) + params.code_address;
const uintptr_t end = (bss_size ? m_kip_header.GetBssAddress() + bss_size : m_kip_header.GetRwAddress() + rw_size) + params.code_address;
const uintptr_t start = (rw_size ? m_kip_header->GetRwAddress() : m_kip_header->GetBssAddress()) + params.code_address;
const uintptr_t end = (bss_size ? m_kip_header->GetBssAddress() + bss_size : m_kip_header->GetRwAddress() + rw_size) + params.code_address;
R_TRY(page_table.SetProcessMemoryPermission(start, util::AlignUp(end - start, PageSize), ams::svc::MemoryPermission_ReadWrite));
}

View File

@@ -27,21 +27,14 @@ namespace ams::kern {
Result KInterruptEvent::Initialize(int32_t interrupt_name, ams::svc::InterruptType type) {
MESOSPHERE_ASSERT_THIS();
/* Verify the interrupt is defined and global. */
R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_name), svc::ResultOutOfRange());
R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_name), svc::ResultOutOfRange());
/* Set interrupt id. */
m_interrupt_id = interrupt_name;
/* Set core id. */
m_core_id = GetCurrentCoreId();
/* Initialize readable event base. */
KReadableEvent::Initialize(nullptr);
/* Try to register the task. */
R_TRY(KInterruptEventTask::Register(m_interrupt_id, m_core_id, type == ams::svc::InterruptType_Level, this));
R_TRY(KInterruptEventTask::Register(m_interrupt_id, type == ams::svc::InterruptType_Level, this));
/* Mark initialized. */
m_is_initialized = true;
@@ -51,7 +44,7 @@ namespace ams::kern {
void KInterruptEvent::Finalize() {
MESOSPHERE_ASSERT_THIS();
g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id, m_core_id);
g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id);
/* Perform inherited finalization. */
KAutoObjectWithSlabHeapAndContainer<KInterruptEvent, KReadableEvent>::Finalize();
@@ -67,12 +60,16 @@ namespace ams::kern {
R_TRY(KReadableEvent::Reset());
/* Clear the interrupt. */
Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id, m_core_id);
Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id);
return ResultSuccess();
}
Result KInterruptEventTask::Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event) {
Result KInterruptEventTask::Register(s32 interrupt_id, bool level, KInterruptEvent *event) {
/* Verify the interrupt id is defined and global. */
R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_id), svc::ResultOutOfRange());
R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_id), svc::ResultOutOfRange());
/* Lock the task table. */
KScopedLightLock lk(g_interrupt_event_lock);
@@ -99,7 +96,7 @@ namespace ams::kern {
KScopedLightLock tlk(task->m_lock);
/* Bind the interrupt handler. */
R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, core_id, KInterruptController::PriorityLevel_High, true, level));
R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, GetCurrentCoreId(), KInterruptController::PriorityLevel_High, true, level));
/* Set the event. */
task->m_event = event;
@@ -115,7 +112,7 @@ namespace ams::kern {
return ResultSuccess();
}
void KInterruptEventTask::Unregister(s32 interrupt_id, s32 core_id) {
void KInterruptEventTask::Unregister(s32 interrupt_id) {
MESOSPHERE_ASSERT_THIS();
/* Lock the task table. */
@@ -130,7 +127,7 @@ namespace ams::kern {
/* Unbind the interrupt. */
m_event = nullptr;
Kernel::GetInterruptManager().UnbindHandler(interrupt_id, core_id);
Kernel::GetInterruptManager().UnbindHandler(interrupt_id, GetCurrentCoreId());
}
KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) {

View File

@@ -83,9 +83,6 @@ namespace ams::kern {
/* Do the task. */
task->DoTask();
/* Destroy any objects we may need to close. */
m_thread->DestroyClosedObjects();
}
}

View File

@@ -35,18 +35,23 @@ namespace ams::kern {
owner_thread->AddWaiter(cur_thread);
/* Set thread states. */
cur_thread->SetState(KThread::ThreadState_Waiting);
if (AMS_LIKELY(cur_thread->GetState() == KThread::ThreadState_Runnable)) {
cur_thread->SetState(KThread::ThreadState_Waiting);
} else {
KScheduler::SetSchedulerUpdateNeeded();
}
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
KScheduler::SetSchedulerUpdateNeeded();
}
}
/* We're no longer waiting on the lock owner. */
{
KScopedSchedulerLock sl;
if (KThread *owner_thread = cur_thread->GetLockOwner(); AMS_UNLIKELY(owner_thread != nullptr)) {
KThread *owner_thread = cur_thread->GetLockOwner();
if (AMS_UNLIKELY(owner_thread)) {
owner_thread->RemoveWaiter(cur_thread);
}
}
@@ -65,13 +70,17 @@ namespace ams::kern {
/* Pass the lock to the next owner. */
uintptr_t next_tag = 0;
if (next_owner != nullptr) {
if (next_owner) {
next_tag = reinterpret_cast<uintptr_t>(next_owner);
if (num_waiters > 1) {
next_tag |= 0x1;
}
next_owner->SetState(KThread::ThreadState_Runnable);
if (AMS_LIKELY(next_owner->GetState() == KThread::ThreadState_Waiting)) {
next_owner->SetState(KThread::ThreadState_Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded();
}
if (next_owner->IsSuspended()) {
next_owner->ContinueIfHasKernelWaiters();

View File

@@ -37,7 +37,7 @@ namespace ams::kern {
/* Create the new region. */
KMemoryRegion *region = std::addressof(this->region_heap[this->num_regions++]);
std::construct_at(region, std::forward<Args>(args)...);
new (region) KMemoryRegion(std::forward<Args>(args)...);
return region;
}
@@ -170,8 +170,14 @@ namespace ams::kern {
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
/* Calculate resource region size based on whether we allow extra threads. */
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
size_t resource_region_size = KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
return KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
/* 10.0.0 reduced the slab heap gaps by 64K. */
if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) {
resource_region_size += (KernelSlabHeapGapsSizeDeprecated - KernelSlabHeapGapsSize);
}
return resource_region_size;
}
}

View File

@@ -100,48 +100,19 @@ namespace ams::kern {
}
/* Free each region to its corresponding heap. */
size_t reserved_sizes[MaxManagerCount] = {};
const uintptr_t ini_start = GetInteger(GetInitialProcessBinaryAddress());
const uintptr_t ini_end = ini_start + InitialProcessBinarySizeMax;
const uintptr_t ini_last = ini_end - 1;
for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) {
if (it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) {
/* Get the manager for the region. */
auto &manager = m_managers[it.GetAttributes()];
/* Check the region. */
MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0);
if (it.GetAddress() <= ini_start && ini_last <= it.GetLastAddress()) {
/* Free memory before the ini to the heap. */
if (it.GetAddress() != ini_start) {
manager.Free(it.GetAddress(), (ini_start - it.GetAddress()) / PageSize);
}
/* Open/reserve the ini memory. */
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
/* Free memory after the ini to the heap. */
if (ini_last != it.GetLastAddress()) {
MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0);
manager.Free(ini_end, it.GetEndAddress() - ini_end);
}
} else {
/* Ensure there's no partial overlap with the ini image. */
if (it.GetAddress() <= ini_last) {
MESOSPHERE_ABORT_UNLESS(it.GetLastAddress() < ini_start);
} else {
/* Otherwise, check the region for general validity. */
MESOSPHERE_ABORT_UNLESS(it.GetEndAddress() != 0);
}
/* Free the memory to the heap. */
manager.Free(it.GetAddress(), it.GetSize() / PageSize);
}
/* Free the memory to the heap. */
m_managers[it.GetAttributes()].Free(it.GetAddress(), it.GetSize() / PageSize);
}
}
/* Update the used size for all managers. */
for (size_t i = 0; i < m_num_managers; ++i) {
m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
m_managers[i].UpdateUsedHeapSize();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -28,19 +28,12 @@ namespace ams::kern {
std::atomic<u64> g_initial_process_id = InitialProcessIdMin;
std::atomic<u64> g_process_id = ProcessIdMin;
Result TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) {
void TerminateChildren(KProcess *process, const KThread *thread_to_not_terminate) {
/* Request that all children threads terminate. */
{
KScopedLightLock proc_lk(process->GetListLock());
KScopedSchedulerLock sl;
if (thread_to_not_terminate != nullptr && process->GetPinnedThread(GetCurrentCoreId()) == thread_to_not_terminate) {
/* NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. */
/* This is valid because the only caller which uses non-nullptr as argument uses GetCurrentThreadPointer(), */
/* but it's still notable because it seems incorrect at first glance. */
process->UnpinCurrentThread();
}
auto &thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread *thread = std::addressof(*it); thread != thread_to_not_terminate) {
@@ -77,14 +70,9 @@ namespace ams::kern {
}
/* Terminate and close the thread. */
ON_SCOPE_EXIT { cur_child->Close(); };
if (Result terminate_result = cur_child->Terminate(); svc::ResultTerminationRequested::Includes(terminate_result)) {
return terminate_result;
}
cur_child->Terminate();
cur_child->Close();
}
return ResultSuccess();
}
}
@@ -218,20 +206,21 @@ namespace ams::kern {
KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy));
/* Clear remaining fields. */
m_num_running_threads = 0;
m_num_process_switches = 0;
m_num_thread_switches = 0;
m_num_fpu_switches = 0;
m_num_supervisor_calls = 0;
m_num_ipc_messages = 0;
m_num_threads = 0;
m_peak_num_threads = 0;
m_num_created_threads = 0;
m_num_process_switches = 0;
m_num_thread_switches = 0;
m_num_fpu_switches = 0;
m_num_supervisor_calls = 0;
m_num_ipc_messages = 0;
m_is_signaled = false;
m_attached_object = nullptr;
m_exception_thread = nullptr;
m_is_suspended = false;
m_memory_release_hint = 0;
m_schedule_count = 0;
m_is_handle_table_initialized = false;
m_is_signaled = false;
m_attached_object = nullptr;
m_exception_thread = nullptr;
m_is_suspended = false;
m_memory_release_hint = 0;
m_schedule_count = 0;
/* We're initialized! */
m_is_initialized = true;
@@ -239,7 +228,7 @@ namespace ams::kern {
return ResultSuccess();
}
Result KProcess::Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool, bool immortal) {
Result KProcess::Initialize(const ams::svc::CreateProcessParameter &params, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(res_limit != nullptr);
MESOSPHERE_ABORT_UNLESS((params.code_num_pages * PageSize) / PageSize == static_cast<size_t>(params.code_num_pages));
@@ -249,7 +238,6 @@ namespace ams::kern {
m_resource_limit = res_limit;
m_system_resource_address = Null<KVirtualAddress>;
m_system_resource_num_pages = 0;
m_is_immortal = immortal;
/* Setup page table. */
/* NOTE: Nintendo passes process ID despite not having set it yet. */
@@ -262,7 +250,7 @@ namespace ams::kern {
auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager());
auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager());
auto *pt_manager = std::addressof(Kernel::GetPageTableManager());
R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager, res_limit));
R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager));
}
auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); };
@@ -298,7 +286,6 @@ namespace ams::kern {
/* Set pool and resource limit. */
m_memory_pool = pool;
m_resource_limit = res_limit;
m_is_immortal = false;
/* Get the memory sizes. */
const size_t code_num_pages = params.code_num_pages;
@@ -367,7 +354,7 @@ namespace ams::kern {
const auto as_type = static_cast<ams::svc::CreateProcessFlag>(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask);
const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr) != 0;
const bool enable_das_merge = (params.flags & ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge) == 0;
R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager, res_limit));
R_TRY(m_page_table.Initialize(m_process_id, as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, params.code_address, code_size, mem_block_manager, block_info_manager, pt_manager));
}
auto pt_guard = SCOPE_GUARD { m_page_table.Finalize(); };
@@ -408,11 +395,6 @@ namespace ams::kern {
/* Terminate child threads. */
TerminateChildren(this, nullptr);
/* Finalize the handle table, if we're not immortal. */
if (!m_is_immortal && m_is_handle_table_initialized) {
this->FinalizeHandleTable();
}
/* Call the debug callback. */
KDebug::OnExitProcess(this);
@@ -420,36 +402,29 @@ namespace ams::kern {
this->FinishTermination();
}
Result KProcess::StartTermination() {
/* Finalize the handle table when we're done, if the process isn't immortal. */
ON_SCOPE_EXIT {
if (!m_is_immortal) {
this->FinalizeHandleTable();
}
};
void KProcess::StartTermination() {
/* Terminate child threads other than the current one. */
return TerminateChildren(this, GetCurrentThreadPointer());
TerminateChildren(this, GetCurrentThreadPointer());
/* Finalize the handle tahble. */
m_handle_table.Finalize();
}
void KProcess::FinishTermination() {
/* Only allow termination to occur if the process isn't immortal. */
if (!m_is_immortal) {
/* Release resource limit hint. */
if (m_resource_limit != nullptr) {
m_memory_release_hint = this->GetUsedUserPhysicalMemorySize();
m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint);
}
/* Change state. */
{
KScopedSchedulerLock sl;
this->ChangeState(State_Terminated);
}
/* Close. */
this->Close();
/* Release resource limit hint. */
if (m_resource_limit != nullptr) {
m_memory_release_hint = this->GetUsedUserPhysicalMemorySize();
m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, 0, m_memory_release_hint);
}
/* Change state. */
{
KScopedSchedulerLock sl;
this->ChangeState(State_Terminated);
}
/* Close. */
this->Close();
}
void KProcess::Exit() {
@@ -510,22 +485,16 @@ namespace ams::kern {
/* If we need to terminate, do so. */
if (needs_terminate) {
/* Start termination. */
if (R_SUCCEEDED(this->StartTermination())) {
/* Note for debug that we're terminating the process. */
MESOSPHERE_LOG("KProcess::Terminate() OK pid=%ld name=%-12s\n", m_process_id, m_name);
this->StartTermination();
/* Call the debug callback. */
KDebug::OnTerminateProcess(this);
/* Note for debug that we're terminating the process. */
MESOSPHERE_LOG("KProcess::Terminate() pid=%ld name=%-12s\n", m_process_id, m_name);
/* Finish termination. */
this->FinishTermination();
} else {
/* Note for debug that we're terminating the process. */
MESOSPHERE_LOG("KProcess::Terminate() FAIL pid=%ld name=%-12s\n", m_process_id, m_name);
/* Call the debug callback. */
KDebug::OnTerminateProcess(this);
/* Register the process as a work task. */
KWorkerTaskManager::AddTask(KWorkerTaskManager::WorkerType_Exit, this);
}
/* Finish termination. */
this->FinishTermination();
}
return ResultSuccess();
@@ -734,16 +703,19 @@ namespace ams::kern {
}
}
void KProcess::IncrementRunningThreadCount() {
MESOSPHERE_ASSERT(m_num_running_threads.load() >= 0);
void KProcess::IncrementThreadCount() {
MESOSPHERE_ASSERT(m_num_threads >= 0);
++m_num_created_threads;
m_num_running_threads.fetch_add(1);
if (const auto count = ++m_num_threads; count > m_peak_num_threads) {
m_peak_num_threads = count;
}
}
void KProcess::DecrementRunningThreadCount() {
MESOSPHERE_ASSERT(m_num_running_threads.load() > 0);
void KProcess::DecrementThreadCount() {
MESOSPHERE_ASSERT(m_num_threads > 0);
if (m_num_running_threads.fetch_sub(1) == 1) {
if (const auto count = --m_num_threads; count == 0) {
this->Terminate();
}
}
@@ -768,22 +740,25 @@ namespace ams::kern {
/* If we have no exception thread, we succeeded. */
if (m_exception_thread == nullptr) {
m_exception_thread = cur_thread;
KScheduler::SetSchedulerUpdateNeeded();
return true;
}
/* Otherwise, wait for us to not have an exception thread. */
cur_thread->SetAddressKey(address_key | 1);
cur_thread->SetAddressKey(address_key);
m_exception_thread->AddWaiter(cur_thread);
cur_thread->SetState(KThread::ThreadState_Waiting);
if (cur_thread->GetState() == KThread::ThreadState_Runnable) {
cur_thread->SetState(KThread::ThreadState_Waiting);
} else {
KScheduler::SetSchedulerUpdateNeeded();
}
}
/* Remove the thread as a waiter from the lock owner. */
{
KScopedSchedulerLock sl;
if (KThread *owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
KThread *owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
KScheduler::SetSchedulerUpdateNeeded();
}
}
}
@@ -804,12 +779,15 @@ namespace ams::kern {
/* Remove waiter thread. */
s32 num_waiters;
if (KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread))); next != nullptr) {
next->SetState(KThread::ThreadState_Runnable);
KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread)));
if (next != nullptr) {
if (next->GetState() == KThread::ThreadState_Waiting) {
next->SetState(KThread::ThreadState_Runnable);
} else {
KScheduler::SetSchedulerUpdateNeeded();
}
}
KScheduler::SetSchedulerUpdateNeeded();
return true;
} else {
return false;
@@ -918,13 +896,13 @@ namespace ams::kern {
R_TRY(m_page_table.SetMaxHeapSize(m_max_process_memory - (m_main_thread_stack_size + m_code_size)));
/* Initialize our handle table. */
R_TRY(this->InitializeHandleTable(m_capabilities.GetHandleTableSize()));
auto ht_guard = SCOPE_GUARD { this->FinalizeHandleTable(); };
R_TRY(m_handle_table.Initialize(m_capabilities.GetHandleTableSize()));
auto ht_guard = SCOPE_GUARD { m_handle_table.Finalize(); };
/* Create a new thread for the process. */
KThread *main_thread = KThread::Create();
R_UNLESS(main_thread != nullptr, svc::ResultOutOfResource());
ON_SCOPE_EXIT { main_thread->Close(); };
auto thread_guard = SCOPE_GUARD { main_thread->Close(); };
/* Initialize the thread. */
R_TRY(KThread::InitializeUserThread(main_thread, reinterpret_cast<KThreadFunction>(GetVoidPointer(this->GetEntryPoint())), 0, stack_top, priority, m_ideal_core_id, this));
@@ -947,11 +925,9 @@ namespace ams::kern {
/* Run our thread. */
R_TRY(main_thread->Run());
/* Open a reference to represent that we're running. */
this->Open();
/* We succeeded! Cancel our guards. */
state_guard.Cancel();
thread_guard.Cancel();
ht_guard.Cancel();
stack_guard.Cancel();
mem_reservation.Commit();
@@ -1027,15 +1003,12 @@ namespace ams::kern {
const s32 core_id = GetCurrentCoreId();
KThread *cur_thread = GetCurrentThreadPointer();
/* If the thread isn't terminated, pin it. */
if (!cur_thread->IsTerminationRequested()) {
/* Pin it. */
this->PinThread(core_id, cur_thread);
cur_thread->Pin();
/* Pin it. */
this->PinThread(core_id, cur_thread);
cur_thread->Pin();
/* An update is needed. */
KScheduler::SetSchedulerUpdateNeeded();
}
/* An update is needed. */
KScheduler::SetSchedulerUpdateNeeded();
}
void KProcess::UnpinCurrentThread() {
@@ -1053,20 +1026,6 @@ namespace ams::kern {
KScheduler::SetSchedulerUpdateNeeded();
}
void KProcess::UnpinThread(KThread *thread) {
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
/* Get the thread's core id. */
const auto core_id = thread->GetActiveCore();
/* Unpin it. */
this->UnpinThread(core_id, thread);
thread->Unpin();
/* An update is needed. */
KScheduler::SetSchedulerUpdateNeeded();
}
Result KProcess::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count) {
/* Lock the list. */
KScopedLightLock lk(m_list_lock);

View File

@@ -146,12 +146,8 @@ namespace ams::kern {
if (m_current_hints[which] + value <= m_limit_values[which] && (timeout < 0 || KHardwareTimer::GetTick() < timeout)) {
m_waiter_count++;
m_cond_var.Wait(&m_lock, timeout, false);
m_cond_var.Wait(&m_lock, timeout);
m_waiter_count--;
if (GetCurrentThread().IsTerminationRequested()) {
return false;
}
} else {
break;
}

View File

@@ -218,7 +218,9 @@ namespace ams::kern {
KThread *task_thread = Kernel::GetInterruptTaskManager().GetThread();
{
KScopedSchedulerLock sl;
task_thread->SetState(KThread::ThreadState_Runnable);
if (AMS_LIKELY(task_thread->GetState() == KThread::ThreadState_Waiting)) {
task_thread->SetState(KThread::ThreadState_Runnable);
}
}
}

View File

@@ -40,7 +40,7 @@ namespace ams::kern {
KServerSession *session = nullptr;
{
KScopedSchedulerLock sl;
if (!m_session_list.empty()) {
while (!m_session_list.empty()) {
session = std::addressof(m_session_list.front());
m_session_list.pop_front();
}
@@ -60,7 +60,7 @@ namespace ams::kern {
KLightServerSession *session = nullptr;
{
KScopedSchedulerLock sl;
if (!m_light_session_list.empty()) {
while (!m_light_session_list.empty()) {
session = std::addressof(m_light_session_list.front());
m_light_session_list.pop_front();
}

View File

@@ -398,17 +398,17 @@ namespace ams::kern {
/* Cleanup Send mappings. */
for (size_t i = 0; i < request->GetSendCount(); ++i) {
R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i)));
R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i), request->GetSendSize(i), request->GetSendMemoryState(i), server_process));
}
/* Cleanup Receive mappings. */
for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i)));
R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i), request->GetReceiveSize(i), request->GetReceiveMemoryState(i), server_process));
}
/* Cleanup Exchange mappings. */
for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i)));
R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i), request->GetExchangeSize(i), request->GetExchangeMemoryState(i), server_process));
}
return ResultSuccess();
@@ -470,7 +470,7 @@ namespace ams::kern {
/* Ensure that we clean up on failure. */
auto setup_guard = SCOPE_GUARD {
dst_page_table.CleanupForIpcServer(dst_address, size, dst_state);
dst_page_table.CleanupForIpcServer(dst_address, size, dst_state, request->GetServerProcess());
src_page_table.CleanupForIpcClient(src_address, size, dst_state);
};
@@ -771,9 +771,6 @@ namespace ams::kern {
/* NOTE: Session is used only for debugging, and so may go unused. */
MESOSPHERE_UNUSED(session);
/* NOTE: Source page table is not used, and so may go unused. */
MESOSPHERE_UNUSED(src_page_table);
/* Determine the message buffers. */
u32 *dst_msg_ptr, *src_msg_ptr;
bool dst_user, src_user;
@@ -910,7 +907,7 @@ namespace ams::kern {
/* If the fast part of the copy didn't get everything, perform the slow part of the copy. */
if (fast_size < raw_size) {
R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
R_TRY(src_page_table.CopyMemoryFromHeapToHeap(dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
dst_perm,
KMemoryAttribute_Uncached, KMemoryAttribute_None,
@@ -924,7 +921,7 @@ namespace ams::kern {
constexpr KMemoryPermission DestinationPermission = static_cast<KMemoryPermission>(KMemoryPermission_NotMapped | KMemoryPermission_KernelReadWrite);
/* Copy the memory. */
R_TRY(dst_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size,
R_TRY(src_page_table.CopyMemoryFromUserToLinear(dst_message_buffer + offset_words, raw_size,
KMemoryState_FlagReferenceCounted, KMemoryState_FlagReferenceCounted,
DestinationPermission,
KMemoryAttribute_Uncached, KMemoryAttribute_None,

View File

@@ -35,7 +35,7 @@ namespace ams::kern {
m_client.Initialize(this);
/* Set state and name. */
this->SetState(State::Normal);
m_state = State::Normal;
m_name = name;
/* Set our owner process. */
@@ -62,8 +62,8 @@ namespace ams::kern {
void KSession::OnServerClosed() {
MESOSPHERE_ASSERT_THIS();
if (this->GetState() == State::Normal) {
this->SetState(State::ServerClosed);
if (m_state == State::Normal) {
m_state = State::ServerClosed;
m_client.OnServerClosed();
}
}
@@ -71,8 +71,8 @@ namespace ams::kern {
void KSession::OnClientClosed() {
MESOSPHERE_ASSERT_THIS();
if (this->GetState() == State::Normal) {
this->SetState(State::ClientClosed);
if (m_state == State::Normal) {
m_state = State::ClientClosed;
m_server.OnClientClosed();
}
}

View File

@@ -19,11 +19,9 @@ namespace ams::kern {
namespace {
constexpr inline s32 TerminatingThreadPriority = ams::svc::SystemThreadPriorityHighest - 1;
constexpr bool IsKernelAddressKey(KProcessAddress key) {
const uintptr_t key_uptr = GetInteger(key);
return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast && (key_uptr & 1) == 0;
return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast;
}
void InitializeKernelStack(uintptr_t stack_top) {
@@ -155,9 +153,8 @@ namespace ams::kern {
m_lock_owner = nullptr;
m_num_core_migration_disables = 0;
/* We have no waiters, and no closed objects. */
/* We have no waiters, but we do have an entrypoint. */
m_num_kernel_waiters = 0;
m_closed_object = nullptr;
/* Set our current core id. */
m_current_core_id = phys_core;
@@ -185,6 +182,7 @@ namespace ams::kern {
if (owner != nullptr) {
m_parent = owner;
m_parent->Open();
m_parent->IncrementThreadCount();
}
/* Initialize thread context. */
@@ -312,6 +310,11 @@ namespace ams::kern {
CleanupKernelStack(reinterpret_cast<uintptr_t>(m_kernel_stack_top));
}
/* Decrement the parent process's thread count. */
if (m_parent != nullptr) {
m_parent->DecrementThreadCount();
}
/* Perform inherited finalization. */
KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize();
}
@@ -423,14 +426,6 @@ namespace ams::kern {
if (active_core != current_core || m_physical_affinity_mask.GetAffinityMask() != m_original_physical_affinity_mask.GetAffinityMask()) {
KScheduler::OnThreadAffinityMaskChanged(this, m_original_physical_affinity_mask, active_core);
}
/* Set base priority-on-unpin. */
const s32 old_base_priority = m_base_priority;
m_base_priority_on_unpin = old_base_priority;
/* Set base priority to higher than any possible process priority. */
m_base_priority = std::min<s32>(old_base_priority, __builtin_ctzll(this->GetOwnerProcess()->GetPriorityMask()));
RestorePriority(this);
}
/* Disallow performing thread suspension. */
@@ -439,7 +434,11 @@ namespace ams::kern {
m_suspend_allowed_flags &= ~(1 << (SuspendType_Thread + ThreadState_SuspendShift));
/* Update our state. */
this->UpdateState();
const ThreadState old_state = m_thread_state;
m_thread_state = static_cast<ThreadState>(this->GetSuspendFlags() | (old_state & ThreadState_Mask));
if (m_thread_state != old_state) {
KScheduler::OnThreadStateChanged(this, old_state);
}
}
/* Update our SVC access permissions. */
@@ -477,24 +476,27 @@ namespace ams::kern {
}
KScheduler::OnThreadAffinityMaskChanged(this, old_mask, active_core);
}
m_base_priority = m_base_priority_on_unpin;
RestorePriority(this);
}
/* Allow performing thread suspension (if termination hasn't been requested). */
if (!this->IsTerminationRequested()) {
{
/* Update our allow flags. */
m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift));
if (!this->IsTerminationRequested()) {
m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift));
}
/* Update our state. */
this->UpdateState();
/* Update our SVC access permissions. */
MESOSPHERE_ASSERT(m_parent != nullptr);
m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters());
const ThreadState old_state = m_thread_state;
m_thread_state = static_cast<ThreadState>(this->GetSuspendFlags() | (old_state & ThreadState_Mask));
if (m_thread_state != old_state) {
KScheduler::OnThreadStateChanged(this, old_state);
}
}
/* Update our SVC access permissions. */
MESOSPHERE_ASSERT(m_parent != nullptr);
m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters());
/* Resume any threads that began waiting on us while we were pinned. */
for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); ++it) {
if (it->GetState() == ThreadState_Waiting) {
@@ -597,16 +599,14 @@ namespace ams::kern {
KScopedSchedulerLock sl;
MESOSPHERE_ASSERT(m_num_core_migration_disables >= 0);
/* If we're updating, set our ideal virtual core. */
if (core_id != ams::svc::IdealCoreNoUpdate) {
m_virtual_ideal_core_id = core_id;
} else {
/* Preserve our ideal core id. */
/* If the core id is no-update magic, preserve the ideal core id. */
if (core_id == ams::svc::IdealCoreNoUpdate) {
core_id = m_virtual_ideal_core_id;
R_UNLESS(((1ul << core_id) & v_affinity_mask) != 0, svc::ResultInvalidCombination());
}
/* Set our affinity mask. */
/* Set the virtual core/affinity mask. */
m_virtual_ideal_core_id = core_id;
m_virtual_affinity_mask = v_affinity_mask;
/* Translate the virtual core to a physical core. */
@@ -708,38 +708,13 @@ namespace ams::kern {
KScopedSchedulerLock sl;
/* Determine the priority value to use. */
const s32 target_priority = m_termination_requested.load() && priority >= TerminatingThreadPriority ? TerminatingThreadPriority : priority;
/* Change our base priority. */
if (this->GetStackParameters().is_pinned) {
m_base_priority_on_unpin = target_priority;
} else {
m_base_priority = target_priority;
}
m_base_priority = priority;
/* Perform a priority restoration. */
RestorePriority(this);
}
void KThread::IncreaseBasePriority(s32 priority) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority);
/* Set our unpin base priority, if we're pinned. */
if (this->GetStackParameters().is_pinned && m_base_priority_on_unpin > priority) {
m_base_priority_on_unpin = priority;
}
/* Set our base priority. */
if (m_base_priority > priority) {
m_base_priority = priority;
/* Perform a priority restoration. */
RestorePriority(this);
}
}
Result KThread::SetPriorityToIdle() {
MESOSPHERE_ASSERT_THIS();
@@ -775,7 +750,11 @@ namespace ams::kern {
m_suspend_request_flags &= ~(1u << (ThreadState_SuspendShift + type));
/* Update our state. */
this->UpdateState();
const ThreadState old_state = m_thread_state;
m_thread_state = static_cast<ThreadState>(this->GetSuspendFlags() | (old_state & ThreadState_Mask));
if (m_thread_state != old_state) {
KScheduler::OnThreadStateChanged(this, old_state);
}
}
void KThread::WaitCancel() {
@@ -811,22 +790,20 @@ namespace ams::kern {
MESOSPHERE_ABORT_UNLESS(this->GetNumKernelWaiters() == 0);
/* Perform the suspend. */
this->UpdateState();
this->Suspend();
}
void KThread::UpdateState() {
void KThread::Suspend() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
MESOSPHERE_ASSERT(this->IsSuspendRequested());
/* Set our suspend flags in state. */
const auto old_state = m_thread_state;
const auto new_state = static_cast<ThreadState>(this->GetSuspendFlags() | (old_state & ThreadState_Mask));
m_thread_state = new_state;
m_thread_state = static_cast<ThreadState>(this->GetSuspendFlags() | (old_state & ThreadState_Mask));
/* Note the state change in scheduler. */
if (new_state != old_state) {
KScheduler::OnThreadStateChanged(this, old_state);
}
KScheduler::OnThreadStateChanged(this, old_state);
}
void KThread::Continue() {
@@ -979,7 +956,6 @@ namespace ams::kern {
/* Keep track of how many kernel waiters we have. */
if (IsKernelAddressKey(thread->GetAddressKey())) {
MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters++) >= 0);
KScheduler::SetSchedulerUpdateNeeded();
}
/* Insert the waiter. */
@@ -994,7 +970,6 @@ namespace ams::kern {
/* Keep track of how many kernel waiters we have. */
if (IsKernelAddressKey(thread->GetAddressKey())) {
MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0);
KScheduler::SetSchedulerUpdateNeeded();
}
/* Remove the waiter. */
@@ -1073,7 +1048,6 @@ namespace ams::kern {
/* Keep track of how many kernel waiters we have. */
if (IsKernelAddressKey(thread->GetAddressKey())) {
MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0);
KScheduler::SetSchedulerUpdateNeeded();
}
it = m_waiter_list.erase(it);
@@ -1120,21 +1094,15 @@ namespace ams::kern {
/* If the current thread has been asked to suspend, suspend it and retry. */
if (GetCurrentThread().IsSuspended()) {
GetCurrentThread().UpdateState();
GetCurrentThread().Suspend();
continue;
}
/* If we're not a kernel thread and we've been asked to suspend, suspend ourselves. */
if (KProcess *parent = this->GetOwnerProcess(); parent != nullptr) {
if (this->IsSuspended()) {
this->UpdateState();
}
parent->IncrementRunningThreadCount();
if (this->IsUserThread() && this->IsSuspended()) {
this->Suspend();
}
/* Open a reference, now that we're running. */
this->Open();
/* Set our state and finish. */
this->SetState(KThread::ThreadState_Runnable);
return ResultSuccess();
@@ -1149,23 +1117,18 @@ namespace ams::kern {
/* Call the debug callback. */
KDebug::OnExitThread(this);
/* Release the thread resource hint, running thread count from parent. */
/* Release the thread resource hint from parent. */
if (m_parent != nullptr) {
m_parent->ReleaseResource(ams::svc::LimitableResource_ThreadCountMax, 0, 1);
m_resource_limit_release_hint = true;
m_parent->DecrementRunningThreadCount();
}
/* Destroy any dependent objects. */
this->DestroyClosedObjects();
/* Perform termination. */
{
KScopedSchedulerLock sl;
/* Disallow all suspension. */
m_suspend_allowed_flags = 0;
this->UpdateState();
/* Start termination. */
this->StartTermination();
@@ -1177,7 +1140,7 @@ namespace ams::kern {
MESOSPHERE_PANIC("KThread::Exit() would return");
}
Result KThread::Terminate() {
void KThread::Terminate() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this != GetCurrentThreadPointer());
@@ -1186,9 +1149,7 @@ namespace ams::kern {
/* If the thread isn't terminated, wait for it to terminate. */
s32 index;
KSynchronizationObject *objects[] = { this };
return KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite);
} else {
return ResultSuccess();
KSynchronizationObject::Wait(std::addressof(index), objects, 1, ams::svc::WaitInfinite);
}
}
@@ -1216,20 +1177,15 @@ namespace ams::kern {
/* Register the terminating dpc. */
this->RegisterDpc(DpcFlag_Terminating);
/* If the thread is pinned, unpin it. */
if (this->GetStackParameters().is_pinned) {
this->GetOwnerProcess()->UnpinThread(this);
}
/* If the thread is suspended, continue it. */
if (this->IsSuspended()) {
m_suspend_allowed_flags = 0;
this->UpdateState();
this->Continue();
}
/* Change the thread's priority to be higher than any system thread's. */
if (this->GetBasePriority() >= ams::svc::SystemThreadPriorityHighest) {
this->SetBasePriority(TerminatingThreadPriority);
this->SetBasePriority(ams::svc::SystemThreadPriorityHighest - 1);
}
/* If the thread is runnable, send a termination interrupt to other cores. */

View File

@@ -23,11 +23,12 @@ namespace ams::kern {
/* Set members. */
m_owner = GetCurrentProcessPointer();
/* Get the owner page table. */
/* Initialize the page group. */
auto &page_table = m_owner->GetPageTable();
new (GetPointer(m_page_group)) KPageGroup(page_table.GetBlockInfoManager());
/* Construct the page group, guarding to make sure our state is valid on exit. */
auto pg_guard = util::ConstructAtGuarded(m_page_group, page_table.GetBlockInfoManager());
/* Ensure that our page group's state is valid on exit. */
auto pg_guard = SCOPE_GUARD { GetReference(m_page_group).~KPageGroup(); };
/* Lock the memory. */
R_TRY(page_table.LockForTransferMemory(GetPointer(m_page_group), addr, size, ConvertToKMemoryPermission(own_perm)));

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