Compare commits

..

14 Commits
0.12.0 ... pgl

Author SHA1 Message Date
Michael Scire
2421ba6c96 pgl: fix minor bugs in impl 2020-04-16 10:09:53 -07:00
Michael Scire
db55e9f6b1 settings: fix duplicate object name 2020-04-16 09:04:55 -07:00
Michael Scire
469e3290f6 pgl: Implement ProcessControlTask 2020-04-16 09:01:43 -07:00
Michael Scire
cfe0597385 pgl: implement LaunchProgramFromHost, GetHostContentMetaInfo 2020-04-16 08:13:46 -07:00
Michael Scire
3b639b604d pgl: Implement (Get)ShellEventObserver 2020-04-16 05:18:31 -07:00
Michael Scire
810b094dc7 pgl: Implement main() 2020-04-16 04:26:59 -07:00
Michael Scire
c2bf630c40 pgl: Implement pgl::srv::Initialize 2020-04-16 03:56:28 -07:00
Michael Scire
25c392676b pgl: implement InitializeProcessControlTask 2020-04-16 03:39:43 -07:00
Michael Scire
48f7697784 pgl: Implement TriggerApplicationSnapShotDumper 2020-04-16 03:28:25 -07:00
Michael Scire
e4653eeaef pgl: implement bool tracking commands 2020-04-16 03:02:23 -07:00
Michael Scire
e53a592f72 pgl: Implement three more commands. 2020-04-16 02:45:36 -07:00
Michael Scire
6ce038acad pgl: begin skeletoning shell service, implement two commands. 2020-04-16 02:33:36 -07:00
Michael Scire
b1aee64b81 pgl: Add service interface for IShellInterface 2020-04-15 21:02:09 -07:00
Michael Scire
1f9cb90923 pgl: add skeleton folder to stratosphere 2020-04-15 20:22:35 -07:00
184 changed files with 860 additions and 5716 deletions

View File

@@ -63,7 +63,6 @@ dist-no-debug: all
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/fatal_errors
mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates
mkdir -p atmosphere-$(AMSVER)/atmosphere/config
@@ -89,7 +88,6 @@ dist-no-debug: all
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
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
@@ -138,7 +136,6 @@ dist: dist-no-debug
cp stratosphere/sm/sm.elf atmosphere-$(AMSVER)-debug/sm.elf
cp stratosphere/spl/spl.elf atmosphere-$(AMSVER)-debug/spl.elf
cp stratosphere/erpt/erpt.elf atmosphere-$(AMSVER)-debug/erpt.elf
cp stratosphere/jpegdec/jpegdec.elf atmosphere-$(AMSVER)-debug/jpegdec.elf
cd atmosphere-$(AMSVER)-debug; zip -r ../atmosphere-$(AMSVER)-debug.zip ./*; cd ../;
rm -r atmosphere-$(AMSVER)-debug
mv atmosphere-$(AMSVER)-debug.zip out/atmosphere-$(AMSVER)-debug.zip

View File

@@ -27,7 +27,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the Atmosphère project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
Credits

View File

@@ -5,6 +5,16 @@ stage2_mtc_path = atmosphere/fusee-mtc.bin
stage2_addr = 0xF0000000
stage2_entrypoint = 0xF0000000
[exosphere]
; Note: Disabling debugmode will cause parts of ams.tma to not work, in the future.
debugmode = 1
debugmode_user = 0
; Note: Disabling usermode exception handlers will cause atmosphere to not fail gracefully under error conditions.
; Support will not be provided to users who disable these. If you do not know what you are doing, leave them on.
disable_user_exception_handlers = 0
; Note: It's currently unknown what effects enabling the usermode PMU register access may have on official code.
enable_user_pmu_access = 0
[stratosphere]
; To force-enable nogc, add nogc = 1
; To force-disable nogc, add nogc = 0

View File

@@ -1,45 +0,0 @@
# Key: debugmode, default: 1.
# Desc: Controls whether kernel is debug mode.
# Disabling this may break Atmosphere's debugger in a future release.
# Key: debugmode_user, default: 0.
# Desc: Controls whether userland is debug mode.
# Key: disable_user_exception_handlers, default: 0.
# Desc: Controls whether user exception handlers are executed on error.
# NOTE: This will cause atmosphere to not fail gracefully.
# Support may not be provided to users tho disable these.
# If you do not know what you are doing, leave them on.
# Key: enable_user_pmu_access, default: 0.
# Desc: Controls whether userland has access to the PMU registers.
# NOTE: It is unknown what effects this has on official code.
# Key: blank_prodinfo_sysmmc, default: 0.
# Desc: Controls whether PRODINFO should be blanked in sysmmc.
# This will cause the system to see dummied out keys and
# serial number information.
# NOTE: This is not known to be safe, as data may be
# cached elsewhere in the system. Usage is not encouraged.
# Key: blank_prodinfo_emummc, default: 0.
# Desc: Controls whether PRODINFO should be blanked in emummc.
# NOTE: This is not known to be safe, as data may be
# cached elsewhere in the system. Usage is not encouraged.
# Key: allow_writing_to_cal_sysmmc, default: 0.
# Desc: Controls whether PRODINFO can be written by homebrew in sysmmc.
# NOTE: Usage of this setting is strongly discouraged without
# a safe backup elsewhere. Turning this on will also cause Atmosphere
# to ensure a safe backup of calibration data is stored in unused
# mmc space, encrypted to prevent detection. This backup can be used
# to prevent unrecoverable edits in emergencies.
[exosphere]
debugmode=1
debugmode_user=0
disable_user_exception_handlers=0
enable_user_pmu_access=0
blank_prodinfo_sysmmc=0
blank_prodinfo_emummc=0
allow_writing_to_cal_sysmmc=0

View File

@@ -1,7 +1,7 @@
# Building Atmosphère
The process for building Atmosphère is similar to building Fusée Gelée payloads and other Switch apps.
In order to build Atmosphère you must have devkitARM and devkitA64 installed on your computer. You can find instructions on how to install and setup devkitARM and devkitA64 on various OSes [here](https://devkitpro.org/wiki/Getting_Started). You'll need to install the following packages via (dkp-)pacman: switch-dev switch-freetype switch-libjpeg-turbo devkitARM devkitarm-rules
In order to build Atmosphère you must have devkitARM and devkitA64 installed on your computer. You can find instructions on how to install and setup devkitARM and devkitA64 on various OSes [here](https://devkitpro.org/wiki/Getting_Started). You'll need to install the following packages via (dkp-)pacman: switch-dev switch-freetype devkitARM devkitarm-rules
sept requires you have python installed with the pycryptodome PyPi packages (`pip install pycryptodome`). You may also want to install the zip package from your package manager of choice to support the `make dist` recipe.

View File

@@ -1,52 +1,4 @@
# Changelog
## 0.12.0
+ Configuration for exosphere was moved to sd:/exosphere.ini.
+ This is to facilitate BIS protection changes described below.
+ Hopefully having this outside of the Atmosphere folder will prevent accidental deletion, since this now contains important settings.
+ Atmosphere's bis protection policy for the PRODINFO partition was substantially reworked.
+ Support was added for "automatically" performing a "blanking" operation to PRODINFO without actually modifying NAND.
+ This is equivalent to using the "incognito" homebrew tool, but NAND is never actually modified.
+ This can be turned on in sysmmc by setting `blank_prodinfo_sysmmc=1` in exosphere.ini, and in emummc by setting `blank_prodinfo_emummc=1` in exosphere.ini.
+ **Please note**: This is not known to be safe. There is a lack of research on whether the information blanked out is cached elsewhere in the system.
+ Usage of this option is not encouraged for this reason.
+ Support was added for writing to the PRODINFO partition, if a verified encrypted backup has been made.
+ PRODINFO is the only system data that cannot be recovered if not backed up, and thus Atmosphere has backed it up to the SD card on boot for some time now.
+ Users who wish to modify their calibration data may now do so unconditionally in emummc, and in sysmmc if `allow_writing_to_cal_sysmmc=1` is set in exosphere.ini.
+ **Please note**: This is heavily discouraged, and the typical user will almost never want to do this.
+ Setting this option will cause Atmosphere to attempt to verify (or create) an encrypted backup of the PRODINFO data to an unused region in the partition.
+ The backup is encrypted with per-console keys that Atmosphere's developers do not know.
+ If the backup is not verified or created, writes will not work. Users who have corrupted their PRODINFO in the past are encouraged to flash a good backup to allow use of this setting.
+ Reads and writes to the region used for the securely encrypted backup will appear to succeed, but will actually read/write from a buffer filled with garbage in memory.
+ Support will be investigated in the future for supporting booting with fully blanked calibration.
+ This is desirable to allow boot to succeed for users who lost their calibration data due to bricking homebrew before bis protection was implemented.
+ `creport` has been updated to use the new screenshot APIs added in 9.0.0+.
+ On 10.0.0+, if a crash occurs in an application (not applet or sysmodule) a screenshot will now be automatically saved to the SD card.
+ If the user applies a patch to vi on 9.0.0 (as the command this uses was previously for dev-units only), this can also work on 9.0.0.
+ The new sysmodule `pgl` added in 10.0.0 was reimplemented.
+ `pgl` ("Program Launcher", probably) is responsible for managing launched user-processes, previously this was handled by NS.
+ The most exciting thing about pgl is that it finally provides an API for multiple clients to subscribe to process events.
+ Using these new APIs, system modules / other homebrew can subscribe to be notified whenever a process event occurs.
+ This means action can be taken on process launch, process exit, process crash, etc.
+ A slight concern with Nintendo's implementation is that each subscriber object uses 0x448 bytes of memory, and N only reserves 8KB for all allocations in pgl.
+ Atmosphere's implementation uses a 32KB heap, which should not be exhaustible.
+ Atmosphere's implementation has a total memory footprint roughly 0x28000 bytes smaller than Nintendo's.
+ A reimplementation was added for the `jpegdec` system module (thanks @HookedBehemoth)!
+ This allows two sessions instead of 1, so homebrew can now use it for software jpeg decoding in addition to the OS itself.
+ As usual the implementation has a very slightly smaller memory footprint than Nintendo's.
+ `dmnt`'s Cheat VM was extended to add three new opcodes.
+ The first new opcode, "ReadWriteStaticRegister", allows for cheats to read from a bank of 128 read-only static registers, and write to a bank of 128 write-only static registers.
+ This can be used in concert with new IPC commands that allow a cheat manager to read or write the value of these static registers to have "dynamic" cheats.
+ As an example, a cheat manager could write a value to a static register that a cheat to control how many of an item to give in a game.
+ As another example, a cheat manager could read a static register that a cheat writes to to learn how many items a player has.
+ The second and third opcodes are a pair, "PauseProcess" and "ResumeProcess".
+ Executing pause process in a cheat will pause the game (it will be frozen) until a resume process opcode is used.
+ These are also available over IPC, for cheat managers or system modules that want to pause or resume the attached cheat process.
+ This allows a cheat to know that the game won't modify or access data the cheat is accessing.
+ For example, this can be used to prevent Pokemon from seeing a pokemon a cheat is in the middle of injecting and turning it into a bad egg.
+ A bug was fixed that would cause the console to crash when connected to Wi-Fi on versions between 3.0.0 and 4.1.0 inclusive.
+ A bug was fixed that could cause boot to fail sporadically due to cache/tlb mismanagement when doing physical ASLR of the kernel.
+ A number of other minor issues were addressed (and more of Atmosphere was updated to reflect other changes in 10.0.x).
+ General system stability improvements to enhance the user's experience.
## 0.11.1
+ A bug was fixed that could cause owls to flicker under certain circumstances.
+ For those interested in technical details, in 10.0.0 kernelldr/kernel no longer set cpuactlr_el1, assuming that it was set correctly by the secure monitor.

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018-2020 Atmosphère-NX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -290,15 +290,7 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
break;
case CONFIGITEM_HAS_RCM_BUG_PATCH:
/* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */
*p_outvalue = (int)(fuse_has_rcm_bug_patch());
break;
case CONFIGITEM_SHOULD_BLANK_PRODINFO:
/* UNOFFICIAL: Gets whether this unit should simulate a "blanked" PRODINFO. */
*p_outvalue = exosphere_should_blank_prodinfo();
break;
case CONFIGITEM_ALLOW_CAL_WRITES:
/* UNOFFICIAL: Gets whether this unit should allow writing to the calibration partition. */
*p_outvalue = exosphere_should_allow_writing_to_cal();
*p_outvalue = (int)(fuse_has_rcm_bug_patch());;
break;
default:
result = 2;

View File

@@ -45,8 +45,6 @@ typedef enum {
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
CONFIGITEM_SHOULD_BLANK_PRODINFO = 65005,
CONFIGITEM_ALLOW_CAL_WRITES = 65006,
} ConfigItem;
#define REBOOT_KIND_NO_REBOOT 0

View File

@@ -27,9 +27,6 @@ static bool g_has_loaded_config = false;
#define EXOSPHERE_CHECK_FLAG(flag) ((g_exosphere_cfg.flags & flag) != 0)
static unsigned int exosphere_is_emummc() {
return g_exosphere_cfg.emummc_cfg.base_cfg.magic == MAGIC_EMUMMC_CONFIG && g_exosphere_cfg.emummc_cfg.base_cfg.type != EMUMMC_TYPE_NONE;
}
/* Read config out of IRAM, return target firmware version. */
unsigned int exosphere_load_config(void) {
@@ -95,26 +92,6 @@ unsigned int exosphere_should_enable_usermode_pmu_access(void) {
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
}
unsigned int exosphere_should_blank_prodinfo(void) {
if (!g_has_loaded_config) {
generic_panic();
}
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_BLANK_PRODINFO);
}
unsigned int exosphere_should_allow_writing_to_cal(void) {
if (!g_has_loaded_config) {
generic_panic();
}
if (exosphere_is_emummc()) {
return 1;
} else {
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC);
}
}
const exo_emummc_config_t *exosphere_get_emummc_config(void) {
if (!g_has_loaded_config) {
generic_panic();

View File

@@ -41,8 +41,6 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {
@@ -62,8 +60,6 @@ unsigned int exosphere_should_override_debugmode_priv(void);
unsigned int exosphere_should_override_debugmode_user(void);
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
unsigned int exosphere_should_enable_usermode_pmu_access(void);
unsigned int exosphere_should_blank_prodinfo(void);
unsigned int exosphere_should_allow_writing_to_cal(void);
const exo_emummc_config_t *exosphere_get_emummc_config(void);

View File

@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
@@ -83,7 +83,7 @@ static void ghash(void *dst, const void *data, size_t data_size, const void *j_b
/* H = aes_ecb_encrypt(zeroes) */
se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, x, 0x10);
size_t total_size = data_size;
while (data_size >= 0x10) {
@@ -103,7 +103,7 @@ static void ghash(void *dst, const void *data, size_t data_size, const void *j_b
if (data_size & 0xF) {
gf128_mul(x, x, h);
}
uint64_t xor_size = total_size << 3;
xor_size = __builtin_bswap64(xor_size);
@@ -113,9 +113,9 @@ static void ghash(void *dst, const void *data, size_t data_size, const void *j_b
} else {
p_x[1] ^= xor_size;
}
gf128_mul(x, x, h);
/* If final output block, XOR with encrypted J block. */
if (encrypt) {
se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, j_block, 0x10);
@@ -140,22 +140,22 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s
generic_panic();
}
}
uint8_t intermediate_buf[0x400] = {0};
/* Unwrap the key */
unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase);
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size);
/* Decrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */
se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, intermediate_buf, dst_size, src + 0x10, src_size - 0x10, src, 0x10);
if (!is_personalized) {
/* Devkit non-personalized keys have no further authentication. */
memcpy(dst, intermediate_buf, src_size - 0x10);
memset(intermediate_buf, 0, sizeof(intermediate_buf));
return src_size - 0x10;
}
}
/* J = GHASH(CTR); */
uint8_t j_block[0x10];
@@ -166,7 +166,7 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s
/* It is supposed to be over the ciphertext. */
uint8_t calc_mac[0x10];
ghash(calc_mac, intermediate_buf, src_size - 0x20, j_block, true);
/* Const-time memcmp. */
const uint8_t *src_bytes = src;
int different = 0;
@@ -184,7 +184,7 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s
if (out_deviceid_high != NULL) {
*out_deviceid_high = intermediate_buf[src_size - 0x28];
}
memcpy(dst, intermediate_buf, src_size - 0x30);
memset(intermediate_buf, 0, sizeof(intermediate_buf));
return src_size - 0x30;
@@ -205,12 +205,10 @@ void gcm_encrypt_key(void *dst, size_t dst_size, const void *src, size_t src_siz
se_generate_random(KEYSLOT_SWITCH_RNGKEY, intermediate_buf, 0x10);
flush_dcache_range(intermediate_buf, intermediate_buf + 0x10);
/* Copy in the src. */
memcpy(intermediate_buf + 0x10, src, src_size);
/* Write Device ID. */
write64be(intermediate_buf, src_size + 0x18, fuse_get_device_id() | (deviceid_high << 56));
/* J = GHASH(CTR); */
uint8_t j_block[0x10];
ghash(j_block, intermediate_buf, 0x10, NULL, false);

View File

@@ -31,8 +31,7 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
#define EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS (1 << 4u)
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {
uint32_t magic;
@@ -51,18 +50,5 @@ _Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t),
#define EXOSPHERE_DEBUGMODE_USER_KEY "debugmode_user"
#define EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY "disable_user_exception_handlers"
#define EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY "enable_user_pmu_access"
#define EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY "blank_prodinfo_sysmmc"
#define EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY "blank_prodinfo_emummc"
#define EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY "allow_writing_to_cal_sysmmc"
typedef struct {
int debugmode;
int debugmode_user;
int disable_user_exception_handlers;
int enable_user_pmu_access;
int blank_prodinfo_sysmmc;
int blank_prodinfo_emummc;
int allow_writing_to_cal_sysmmc;
} exosphere_parse_cfg_t;
#endif
#endif

View File

@@ -134,57 +134,38 @@ static int emummc_ini_handler(void *user, const char *section, const char *name,
}
static int exosphere_ini_handler(void *user, const char *section, const char *name, const char *value) {
exosphere_parse_cfg_t *parse_cfg = (exosphere_parse_cfg_t *)user;
exosphere_config_t *exo_cfg = (exosphere_config_t *)user;
int tmp = 0;
if (strcmp(section, "exosphere") == 0) {
if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
if (strcmp(name, EXOSPHERE_TARGETFW_KEY) == 0) {
sscanf(value, "%ld", &exo_cfg->target_firmware);
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->debugmode = 1;
} else if (tmp == 0) {
parse_cfg->debugmode = 0;
if (tmp) {
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
} else {
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV);
}
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_USER_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->debugmode_user = 1;
} else if (tmp == 0) {
parse_cfg->debugmode_user = 0;
if (tmp) {
exo_cfg->flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
} else {
exo_cfg->flags &= ~(EXOSPHERE_FLAG_IS_DEBUGMODE_USER);
}
} else if (strcmp(name, EXOSPHERE_DISABLE_USERMODE_EXCEPTION_HANDLERS_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->disable_user_exception_handlers = 1;
} else if (tmp == 0) {
parse_cfg->disable_user_exception_handlers = 0;
if (tmp) {
exo_cfg->flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
} else {
exo_cfg->flags &= ~(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
}
} else if (strcmp(name, EXOSPHERE_ENABLE_USERMODE_PMU_ACCESS_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->enable_user_pmu_access = 1;
} else if (tmp == 0) {
parse_cfg->enable_user_pmu_access = 0;
}
} else if (strcmp(name, EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->blank_prodinfo_sysmmc = 1;
} else if (tmp == 0) {
parse_cfg->blank_prodinfo_sysmmc = 0;
}
} else if (strcmp(name, EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->blank_prodinfo_emummc = 1;
} else if (tmp == 0) {
parse_cfg->blank_prodinfo_emummc = 0;
}
} else if (strcmp(name, EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->allow_writing_to_cal_sysmmc = 1;
} else if (tmp == 0) {
parse_cfg->allow_writing_to_cal_sysmmc = 0;
if (tmp) {
exo_cfg->flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
} else {
exo_cfg->flags &= ~(EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS);
}
} else {
return 0;
@@ -367,42 +348,15 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
exo_cfg.target_firmware = target_firmware;
memcpy(&exo_cfg.emummc_cfg, exo_emummc_cfg, sizeof(*exo_emummc_cfg));
const bool is_emummc = exo_emummc_cfg->base_cfg.magic == MAGIC_EMUMMC_CONFIG && exo_emummc_cfg->base_cfg.type != EMUMMC_TYPE_NONE;
if (keygen_type) {
exo_cfg.flags = EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
} else {
exo_cfg.flags = 0;
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT;
}
/* Setup exosphere parse configuration with defaults. */
exosphere_parse_cfg_t parse_cfg = {
.debugmode = 1,
.debugmode_user = 0,
.disable_user_exception_handlers = 0,
.enable_user_pmu_access = 0,
.blank_prodinfo_sysmmc = 0,
.blank_prodinfo_emummc = 0,
.allow_writing_to_cal_sysmmc = 0,
};
/* If we have an ini to read, parse it. */
char *exosphere_ini = calloc(1, 0x10000);
if (read_from_file(exosphere_ini, 0xFFFF, "exosphere.ini")) {
if (ini_parse_string(exosphere_ini, exosphere_ini_handler, &parse_cfg) < 0) {
fatal_error("[NXBOOT] Failed to parse exosphere.ini!\n");
}
if (ini_parse_string(get_loader_ctx()->bct0, exosphere_ini_handler, &exo_cfg) < 0) {
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
}
free(exosphere_ini);
/* Apply parse config. */
if (parse_cfg.debugmode) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
if (parse_cfg.debugmode_user) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
if (parse_cfg.disable_user_exception_handlers) exo_cfg.flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
if (parse_cfg.enable_user_pmu_access) exo_cfg.flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
if (parse_cfg.blank_prodinfo_sysmmc && !is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO;
if (parse_cfg.blank_prodinfo_emummc && is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO;
if (parse_cfg.allow_writing_to_cal_sysmmc) exo_cfg.flags |= EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC;
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");

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = bb40dae329450407a24db2c3c4edd4ed1fd46532
parent = 745887955507cf42361f37aacfe5ca5477b8e5d5
commit = 96825c7524c333536dcffadb76341bf599785538
parent = d81a3bdc36ddf3d7b4f3d7fae5cb85afc047b546
method = merge
cmdver = 0.4.1

View File

@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the atmosphere-libs project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
Credits

View File

@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libmesosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
Credits

View File

@@ -221,14 +221,14 @@ namespace ams::kern::arch::arm64::init {
PageTableEntry *src_entry = this->GetMappingEntry(src_virt_addr, block_size);
const auto src_saved = *src_entry;
for (size_t i = 0; i < num_mappings; i++) {
src_entry[i] = InvalidPageTableEntry;
*src_entry = InvalidPageTableEntry;
}
/* Unmap the target. */
PageTableEntry *dst_entry = this->GetMappingEntry(dst_virt_addr, block_size);
const auto dst_saved = *dst_entry;
for (size_t i = 0; i < num_mappings; i++) {
dst_entry[i] = InvalidPageTableEntry;
*dst_entry = InvalidPageTableEntry;
}
/* Invalidate the entire tlb. */
@@ -237,7 +237,7 @@ namespace ams::kern::arch::arm64::init {
/* Copy data, if we should. */
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
const u64 offset_mask = negative_block_size_for_mask & ((1ul << 48) - 1);
const u64 offset_mask = negative_block_size_for_mask & ((1ul << 36) - 1);
const KVirtualAddress copy_src_addr = KVirtualAddress(src_saved.GetRawAttributesUnsafeForSwap() & offset_mask);
const KVirtualAddress copy_dst_addr = KVirtualAddress(dst_saved.GetRawAttributesUnsafeForSwap() & offset_mask);
if (block_size && do_copy) {
@@ -250,7 +250,7 @@ namespace ams::kern::arch::arm64::init {
}
/* Swap the mappings. */
const u64 attr_preserve_mask = (negative_block_size_for_mask | 0xFFFF000000000000ul) ^ ((1ul << 48) - 1);
const u64 attr_preserve_mask = (negative_block_size_for_mask | 0xFFFF000000000000ul) ^ ((1ul << 36) - 1);
const size_t shift_for_contig = contig ? 4 : 0;
size_t advanced_size = 0;
const u64 src_attr_val = src_saved.GetRawAttributesUnsafeForSwap() & attr_preserve_mask;
@@ -291,8 +291,8 @@ namespace ams::kern::arch::arm64::init {
L1PageTableEntry *l1_entry = GetL1Entry(this->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) {
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, false);
if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && util::IsAligned(size, L1BlockSize)) {
*l1_entry = L1PageTableEntry(phys_addr, attr, false);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L1BlockSize;
@@ -305,16 +305,16 @@ namespace ams::kern::arch::arm64::init {
if (!l1_entry->IsTable()) {
KPhysicalAddress new_table = allocator.Allocate();
ClearNewPageTable(new_table);
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
*l1_entry = L1PageTableEntry(new_table, attr.IsPrivilegedExecuteNever());
cpu::DataSynchronizationBarrierInnerShareable();
}
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
/* Can we make a contiguous L2 block? */
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && util::IsAligned(size, L2ContiguousBlockSize)) {
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, true);
l2_entry[i] = L2PageTableEntry(phys_addr, attr, true);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L2BlockSize;
@@ -325,8 +325,8 @@ namespace ams::kern::arch::arm64::init {
}
/* Can we make an L2 block? */
if (util::IsAligned(GetInteger(virt_addr), L2BlockSize) && util::IsAligned(GetInteger(phys_addr), L2BlockSize) && size >= L2BlockSize) {
*l2_entry = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, false);
if (util::IsAligned(GetInteger(virt_addr), L2BlockSize) && util::IsAligned(GetInteger(phys_addr), L2BlockSize) && util::IsAligned(size, L2BlockSize)) {
*l2_entry = L2PageTableEntry(phys_addr, attr, false);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L2BlockSize;
@@ -339,16 +339,16 @@ namespace ams::kern::arch::arm64::init {
if (!l2_entry->IsTable()) {
KPhysicalAddress new_table = allocator.Allocate();
ClearNewPageTable(new_table);
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
*l2_entry = L2PageTableEntry(new_table, attr.IsPrivilegedExecuteNever());
cpu::DataSynchronizationBarrierInnerShareable();
}
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
/* Can we make a contiguous L3 block? */
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && util::IsAligned(size, L3ContiguousBlockSize)) {
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, true);
l3_entry[i] = L3PageTableEntry(phys_addr, attr, true);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L3BlockSize;
@@ -359,7 +359,7 @@ namespace ams::kern::arch::arm64::init {
}
/* Make an L3 block. */
*l3_entry = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, false);
*l3_entry = L3PageTableEntry(phys_addr, attr, false);
cpu::DataSynchronizationBarrierInnerShareable();
virt_addr += L3BlockSize;
phys_addr += L3BlockSize;
@@ -394,77 +394,6 @@ namespace ams::kern::arch::arm64::init {
return l3_entry->GetBlock() + (GetInteger(virt_addr) & (L3BlockSize - 1));
}
KPhysicalAddress GetPhysicalAddressOfRandomizedRange(KVirtualAddress virt_addr, size_t size) const {
/* Define tracking variables for ourselves to use. */
KPhysicalAddress min_phys_addr = Null<KPhysicalAddress>;
KPhysicalAddress max_phys_addr = Null<KPhysicalAddress>;
/* Ensure the range we're querying is valid. */
const KVirtualAddress end_virt_addr = virt_addr + size;
if (virt_addr > end_virt_addr) {
MESOSPHERE_INIT_ABORT_UNLESS(size == 0);
return min_phys_addr;
}
auto UpdateExtents = [&](const KPhysicalAddress block, size_t block_size) ALWAYS_INLINE_LAMBDA {
/* Ensure that we are allowed to have the block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), block_size));
MESOSPHERE_INIT_ABORT_UNLESS(block_size <= GetInteger(end_virt_addr) - GetInteger(virt_addr));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), block_size));
MESOSPHERE_INIT_ABORT_UNLESS(size >= block_size);
const KPhysicalAddress block_end = block + block_size;
/* We want to update min phys addr when it's 0 or > block. */
/* This is equivalent in two's complement to (n - 1) >= block. */
if ((GetInteger(min_phys_addr) - 1) >= GetInteger(block)) {
min_phys_addr = block;
}
/* Update max phys addr when it's 0 or < block_end. */
if (GetInteger(max_phys_addr) < GetInteger(block_end) || GetInteger(max_phys_addr) == 0) {
max_phys_addr = block_end;
}
/* Traverse onwards. */
virt_addr += block_size;
};
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
/* If an L1 block is mapped, update. */
if (l1_entry->IsBlock()) {
UpdateExtents(l1_entry->GetBlock(), L1BlockSize);
continue;
}
/* Not a block, so we must have a table. */
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
UpdateExtents(l2_entry->GetBlock(), l2_entry->IsContiguous() ? L2ContiguousBlockSize : L2BlockSize);
continue;
}
/* Not a block, so we must have a table. */
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* We must have a mapped l3 entry to inspect. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
UpdateExtents(l3_entry->GetBlock(), l3_entry->IsContiguous() ? L3ContiguousBlockSize : L3BlockSize);
}
/* Ensure we got the right range. */
MESOSPHERE_INIT_ABORT_UNLESS(GetInteger(max_phys_addr) - GetInteger(min_phys_addr) == size);
/* Write the address that we found. */
return min_phys_addr;
}
bool IsFree(KVirtualAddress virt_addr, size_t size) {
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
@@ -528,7 +457,7 @@ namespace ams::kern::arch::arm64::init {
/* Ensure that we are allowed to have an L1 block here. */
const KPhysicalAddress block = l1_entry->GetBlock();
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(size >= L1BlockSize);
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L1 block. */
@@ -537,7 +466,7 @@ namespace ams::kern::arch::arm64::init {
cpu::InvalidateEntireTlb();
/* Create new L1 block. */
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, block, attr_after, false);
*l1_entry = L1PageTableEntry(block, attr_after, false);
virt_addr += L1BlockSize;
size -= L1BlockSize;
@@ -555,7 +484,7 @@ namespace ams::kern::arch::arm64::init {
/* Ensure that we are allowed to have a contiguous L2 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(size >= L2ContiguousBlockSize);
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2ContiguousBlockSize));
/* Invalidate the existing contiguous L2 block. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
@@ -568,7 +497,7 @@ namespace ams::kern::arch::arm64::init {
/* Create a new contiguous L2 block. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, block + L2BlockSize * i, attr_after, true);
l2_entry[i] = L2PageTableEntry(block + L2BlockSize * i, attr_after, true);
}
virt_addr += L2ContiguousBlockSize;
@@ -577,7 +506,7 @@ namespace ams::kern::arch::arm64::init {
/* Ensure that we are allowed to have an L2 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(size >= L2BlockSize);
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L2 block. */
@@ -586,7 +515,7 @@ namespace ams::kern::arch::arm64::init {
cpu::InvalidateEntireTlb();
/* Create new L2 block. */
*l2_entry = L2PageTableEntry(PageTableEntry::BlockTag{}, block, attr_after, false);
*l2_entry = L2PageTableEntry(block, attr_after, false);
virt_addr += L2BlockSize;
size -= L2BlockSize;
@@ -607,7 +536,7 @@ namespace ams::kern::arch::arm64::init {
/* Ensure that we are allowed to have a contiguous L3 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(size >= L3ContiguousBlockSize);
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3ContiguousBlockSize));
/* Invalidate the existing contiguous L3 block. */
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
@@ -620,7 +549,7 @@ namespace ams::kern::arch::arm64::init {
/* Create a new contiguous L3 block. */
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, block + L3BlockSize * i, attr_after, true);
l3_entry[i] = L3PageTableEntry(block + L3BlockSize * i, attr_after, true);
}
virt_addr += L3ContiguousBlockSize;
@@ -629,7 +558,7 @@ namespace ams::kern::arch::arm64::init {
/* Ensure that we are allowed to have an L3 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(size >= L3BlockSize);
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L3 block. */
@@ -638,7 +567,7 @@ namespace ams::kern::arch::arm64::init {
cpu::InvalidateEntireTlb();
/* Create new L3 block. */
*l3_entry = L3PageTableEntry(PageTableEntry::BlockTag{}, block, attr_after, false);
*l3_entry = L3PageTableEntry(block, attr_after, false);
virt_addr += L3BlockSize;
size -= L3BlockSize;

View File

@@ -156,7 +156,6 @@ namespace ams::kern::arch::arm64::cpu {
void ClearPageToZeroImpl(void *);
void FlushEntireDataCacheSharedForInit();
void FlushEntireDataCacheLocalForInit();
void StoreEntireCacheForInit();
void FlushEntireDataCache();

View File

@@ -104,7 +104,7 @@ namespace ams::kern::arch::arm64 {
private:
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
/* Set basic attributes. */
PageTableEntry entry{PageTableEntry::ExtensionFlag_Valid};
PageTableEntry entry;
entry.SetPrivilegedExecuteNever(true);
entry.SetAccessFlag(PageTableEntry::AccessFlag_Accessed);
entry.SetShareable(PageTableEntry::Shareable_InnerShareable);
@@ -163,9 +163,6 @@ namespace ams::kern::arch::arm64 {
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Set the fault bit based on whether the page is mapped. */
entry.SetMapped((properties.perm & KMemoryPermission_NotMapped) == 0);
return entry;
}
public:
@@ -261,6 +258,8 @@ namespace ams::kern::arch::arm64 {
}
}
ClearPageTable(table);
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
return table;

View File

@@ -30,8 +30,12 @@ namespace ams::kern::arch::arm64 {
class PageTableEntry {
public:
struct InvalidTag{};
struct TableTag{};
struct BlockTag{};
enum ExtensionTag : u64 {
ExtensionTag_IsValidBit = (1ul << 56),
ExtensionTag_IsValid = (ExtensionTag_IsValidBit | (1ul << 0)),
ExtensionTag_IsBlockMask = (ExtensionTag_IsValidBit | (1ul << 1)),
};
enum Permission : u64 {
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
@@ -64,26 +68,13 @@ namespace ams::kern::arch::arm64 {
AccessFlag_Accessed = (1 << 10),
};
enum MappingFlag : u64 {
MappingFlag_NotMapped = (0 << 0),
MappingFlag_Mapped = (1 << 0),
};
enum ExtensionFlag : u64 {
ExtensionFlag_NotContiguous = (1ul << 55),
ExtensionFlag_Valid = (1ul << 56),
ExtensionFlag_ValidAndMapped = (ExtensionFlag_Valid | MappingFlag_Mapped),
ExtensionFlag_TestTableMask = (ExtensionFlag_Valid | (1ul << 1)),
};
enum Type : u64 {
Type_None = 0x0,
Type_L1Block = ExtensionFlag_Valid,
Type_L1Table = 0x2,
Type_L2Block = ExtensionFlag_Valid,
Type_L2Table = 0x2,
Type_L3Block = ExtensionFlag_TestTableMask,
Type_L1Block = 0x1,
Type_L1Table = 0x3,
Type_L2Block = 0x1,
Type_L2Table = 0x3,
Type_L3Block = 0x3,
};
enum ContigType : u64 {
@@ -94,17 +85,17 @@ namespace ams::kern::arch::arm64 {
u64 attributes;
public:
/* Take in a raw attribute. */
constexpr explicit ALWAYS_INLINE PageTableEntry() : attributes() { /* ... */ }
constexpr explicit ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry() : attributes() { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
constexpr explicit ALWAYS_INLINE PageTableEntry(InvalidTag) : attributes(0) { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(InvalidTag) : attributes(0) { /* ... */ }
/* Extend a previous attribute. */
constexpr explicit ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
/* Construct a new attribute. */
constexpr explicit ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share, MappingFlag m)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share) | static_cast<u64>(ExtensionFlag_Valid) | static_cast<u64>(m))
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share) | static_cast<u64>(ExtensionTag_IsValid))
{
/* ... */
}
@@ -138,7 +129,7 @@ namespace ams::kern::arch::arm64 {
}
}
public:
constexpr ALWAYS_INLINE bool IsContiguousAllowed() const { return this->GetBits(55, 1) == 0; }
constexpr ALWAYS_INLINE bool IsContiguousAllowed() const { return this->GetBits(55, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
@@ -149,10 +140,9 @@ namespace ams::kern::arch::arm64 {
constexpr ALWAYS_INLINE bool IsReadOnly() const { return this->GetBits(7, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserAccessible() const { return this->GetBits(6, 1) != 0; }
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return (this->attributes & ExtensionFlag_TestTableMask) == ExtensionFlag_Valid; }
constexpr ALWAYS_INLINE bool IsTable() const { return (this->attributes & ExtensionFlag_TestTableMask) == 2; }
constexpr ALWAYS_INLINE bool IsEmpty() const { return (this->attributes & ExtensionFlag_TestTableMask) == 0; }
constexpr ALWAYS_INLINE bool IsMapped() const { return this->GetBits(0, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return (this->attributes & ExtensionTag_IsBlockMask) == ExtensionTag_IsValidBit; }
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE bool IsEmpty() const { return this->GetBits(0, 2) == 0x0; }
constexpr ALWAYS_INLINE decltype(auto) SetContiguousAllowed(bool en) { this->SetBit(55, !en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserExecuteNever(bool en) { this->SetBit(54, en); return *this; }
@@ -164,10 +154,9 @@ namespace ams::kern::arch::arm64 {
constexpr ALWAYS_INLINE decltype(auto) SetReadOnly(bool en) { this->SetBit(7, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserAccessible(bool en) { this->SetBit(6, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetPageAttribute(PageAttribute a) { this->SetBitsDirect(2, 3, a); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetMapped(bool m) { static_assert(static_cast<u64>(MappingFlag_Mapped == (1 << 0))); this->SetBit(0, m); return *this; }
constexpr ALWAYS_INLINE u64 GetEntryTemplate() const {
constexpr u64 Mask = (0xFFF0000000000FFFul & ~u64((0x1ul << 52) | ExtensionFlag_TestTableMask));
constexpr u64 Mask = (0xFFF0000000000FFFul & ~u64(0x3ul | (0x1ul << 52)));
return this->attributes & Mask;
}
@@ -193,22 +182,22 @@ namespace ams::kern::arch::arm64 {
class L1PageTableEntry : public PageTableEntry {
public:
constexpr explicit ALWAYS_INLINE L1PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L1PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr explicit ALWAYS_INLINE L1PageTableEntry(TableTag, KPhysicalAddress phys_addr, bool pxn)
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr explicit ALWAYS_INLINE L1PageTableEntry(TableTag, KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr explicit ALWAYS_INLINE L1PageTableEntry(BlockTag, KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionFlag_Valid)
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
@@ -232,28 +221,28 @@ namespace ams::kern::arch::arm64 {
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L1PageTableEntry(BlockTag{}, this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L2PageTableEntry : public PageTableEntry {
public:
constexpr explicit ALWAYS_INLINE L2PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L2PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr explicit ALWAYS_INLINE L2PageTableEntry(TableTag, KPhysicalAddress phys_addr, bool pxn)
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr explicit ALWAYS_INLINE L2PageTableEntry(TableTag, KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr explicit ALWAYS_INLINE L2PageTableEntry(BlockTag, KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionFlag_Valid)
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
@@ -277,21 +266,21 @@ namespace ams::kern::arch::arm64 {
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L2PageTableEntry(BlockTag{}, this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L3PageTableEntry : public PageTableEntry {
public:
constexpr explicit ALWAYS_INLINE L3PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L3PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr explicit ALWAYS_INLINE L3PageTableEntry(BlockTag, KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | static_cast<u64>(ExtensionFlag_TestTableMask))
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x2 | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
constexpr ALWAYS_INLINE bool IsBlock() const { return (GetRawAttributes() & ExtensionFlag_TestTableMask) == ExtensionFlag_TestTableMask; }
constexpr ALWAYS_INLINE bool IsBlock() const { return (GetRawAttributes() & ExtensionTag_IsBlockMask) == ExtensionTag_IsBlockMask; }
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(12, 36);
@@ -299,7 +288,7 @@ namespace ams::kern::arch::arm64 {
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L3PageTableEntry(BlockTag{}, this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};

View File

@@ -26,27 +26,27 @@ namespace ams::kern::arch::arm64 {
constexpr KNotAlignedSpinLock() : packed_tickets(0) { /* ... */ }
void Lock() {
u32 tmp0, tmp1, tmp2;
u32 tmp0, tmp1;
__asm__ __volatile__(
" prfm pstl1keep, %[packed_tickets]\n"
"1:\n"
" ldaxr %w[tmp0], %[packed_tickets]\n"
" add %w[tmp2], %w[tmp0], #0x10000\n"
" stxr %w[tmp1], %w[tmp2], %[packed_tickets]\n"
" add %w[tmp0], %w[tmp0], #0x10000\n"
" stxr %w[tmp1], %w[tmp0], %[packed_tickets]\n"
" cbnz %w[tmp1], 1b\n"
" \n"
" and %w[tmp1], %w[tmp0], #0xFFFF\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.eq 3f\n"
" b.eq done"
" sevl\n"
"2:\n"
" wfe\n"
" ldaxrh %w[tmp1], %[packed_tickets]\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.ne 2b\n"
"3:\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [tmp2]"=&r"(tmp2), [packed_tickets]"+Q"(this->packed_tickets)
"done:\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [packed_tickets]"+Q"(this->packed_tickets)
:
: "cc", "memory"
);
@@ -106,6 +106,6 @@ namespace ams::kern::arch::arm64 {
};
static_assert(sizeof(KAlignedSpinLock) == 2 * cpu::DataCacheLineSize);
using KSpinLock = KNotAlignedSpinLock;
using KSpinLock = KAlignedSpinLock;
}

View File

@@ -44,7 +44,6 @@ namespace ams::kern::board::nintendo::nx {
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
/* Privileged Access. */
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);

View File

@@ -204,9 +204,9 @@ namespace ams::kern {
u8 irq_access_flags[IrqFlagCount]{};
u64 core_mask{};
u64 priority_mask{};
util::BitPack32 debug_capabilities{0};
util::BitPack32 debug_capabilities;
s32 handle_table_size{};
util::BitPack32 intended_kernel_version{0};
util::BitPack32 intended_kernel_version;
u32 program_type{};
private:
static constexpr ALWAYS_INLINE void SetSvcAllowedImpl(u8 *data, u32 id) {
@@ -254,7 +254,7 @@ namespace ams::kern {
Result SetCapability(const util::BitPack32 cap, u32 &set_flags, u32 &set_svc, KProcessPageTable *page_table);
Result SetCapabilities(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
public:
constexpr KCapabilities() = default;
constexpr KCapabilities() : debug_capabilities(0), intended_kernel_version(0) { /* ... */ }
Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_slab_heap.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_memory_block.hpp>
#include <mesosphere/kern_k_page_bitmap.hpp>
#include <mesosphere/kern_select_interrupt_manager.hpp>
namespace ams::kern {
class KDynamicPageManager {
public:
class PageBuffer {
private:
u8 buffer[PageSize];
};
static_assert(sizeof(PageBuffer) == PageSize);
private:
KSpinLock lock;
KPageBitmap page_bitmap;
size_t used;
size_t peak;
size_t count;
KVirtualAddress address;
size_t size;
public:
KDynamicPageManager() : lock(), page_bitmap(), used(), peak(), count(), address(), size() { /* ... */ }
Result Initialize(KVirtualAddress memory, size_t sz) {
/* We need to have positive size. */
R_UNLESS(sz > 0, svc::ResultOutOfMemory());
/* Calculate metadata overhead. */
const size_t metadata_size = KPageBitmap::CalculateMetadataOverheadSize(sz / sizeof(PageBuffer));
const size_t allocatable_size = sz - metadata_size;
/* Set tracking fields. */
this->address = memory;
this->size = util::AlignDown(allocatable_size, sizeof(PageBuffer));
this->count = allocatable_size / sizeof(PageBuffer);
R_UNLESS(this->count > 0, svc::ResultOutOfMemory());
/* Clear the metadata region. */
u64 *metadata_ptr = GetPointer<u64>(this->address + allocatable_size);
std::memset(metadata_ptr, 0, metadata_size);
/* Initialize the bitmap. */
this->page_bitmap.Initialize(metadata_ptr, this->count);
/* Free the pages to the bitmap. */
std::memset(GetPointer<PageBuffer>(this->address), 0, this->count * sizeof(PageBuffer));
for (size_t i = 0; i < this->count; i++) {
this->page_bitmap.SetBit(i);
}
return ResultSuccess();
}
constexpr KVirtualAddress GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
constexpr size_t GetUsed() const { return this->used; }
constexpr size_t GetPeak() const { return this->peak; }
constexpr size_t GetCount() const { return this->count; }
PageBuffer *Allocate() {
/* Take the lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(this->lock);
/* Find a random free block. */
ssize_t soffset = this->page_bitmap.FindFreeBlock(true);
if (AMS_UNLIKELY(soffset < 0)) {
return nullptr;
}
const size_t offset = static_cast<size_t>(soffset);
/* Update our tracking. */
this->page_bitmap.ClearBit(offset);
this->peak = std::max(this->peak, (++this->used));
return GetPointer<PageBuffer>(this->address) + offset;
}
void Free(PageBuffer *pb) {
/* Take the lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(this->lock);
/* Set the bit for the free page. */
size_t offset = (reinterpret_cast<uintptr_t>(pb) - GetInteger(this->address)) / sizeof(PageBuffer);
this->page_bitmap.SetBit(offset);
/* Decrement our used count. */
--this->used;
}
};
}

View File

@@ -18,20 +18,29 @@
#include <mesosphere/kern_k_slab_heap.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_memory_block.hpp>
#include <mesosphere/kern_k_dynamic_page_manager.hpp>
namespace ams::kern {
namespace impl {
class DynamicSlabHeapPage {
private:
u8 buffer[PageSize];
};
static_assert(sizeof(DynamicSlabHeapPage) == PageSize);
};
template<typename T>
class KDynamicSlabHeap {
NON_COPYABLE(KDynamicSlabHeap);
NON_MOVEABLE(KDynamicSlabHeap);
private:
using Impl = impl::KSlabHeapImpl;
using PageBuffer = KDynamicPageManager::PageBuffer;
using PageBuffer = impl::DynamicSlabHeapPage;
private:
Impl impl;
KDynamicPageManager *page_allocator;
KDynamicSlabHeap<PageBuffer> *next_allocator;
std::atomic<size_t> used;
std::atomic<size_t> peak;
std::atomic<size_t> count;
@@ -45,7 +54,7 @@ namespace ams::kern {
return std::addressof(this->impl);
}
public:
constexpr KDynamicSlabHeap() : impl(), page_allocator(), used(), peak(), count(), address(), size() { /* ... */ }
constexpr KDynamicSlabHeap() : impl(), next_allocator(), used(), peak(), count(), address(), size() { /* ... */ }
constexpr KVirtualAddress GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
@@ -71,27 +80,10 @@ namespace ams::kern {
}
}
void Initialize(KDynamicPageManager *page_allocator) {
this->page_allocator = page_allocator;
this->address = this->page_allocator->GetAddress();
this->size = this->page_allocator->GetSize();
}
void Initialize(KDynamicPageManager *page_allocator, size_t num_objects) {
MESOSPHERE_ASSERT(page_allocator != nullptr);
/* Initialize members. */
this->Initialize(page_allocator);
/* Allocate until we have the correct number of objects. */
while (this->count < num_objects) {
auto *allocated = reinterpret_cast<T *>(this->page_allocator->Allocate());
MESOSPHERE_ABORT_UNLESS(allocated != nullptr);
for (size_t i = 0; i < sizeof(PageBuffer) / sizeof(T); i++) {
this->GetImpl()->Free(allocated + i);
}
this->count += sizeof(PageBuffer) / sizeof(T);
}
void Initialize(KDynamicSlabHeap<PageBuffer> *next) {
this->next_allocator = next;
this->address = next->GetAddress();
this->size = next->GetSize();
}
T *Allocate() {
@@ -99,8 +91,8 @@ namespace ams::kern {
/* If we fail to allocate, try to get a new page from our next allocator. */
if (AMS_UNLIKELY(allocated == nullptr)) {
if (this->page_allocator != nullptr) {
allocated = reinterpret_cast<T *>(this->page_allocator->Allocate());
if (this->next_allocator != nullptr) {
allocated = reinterpret_cast<T *>(this->next_allocator->Allocate());
if (allocated != nullptr) {
/* If we succeeded in getting a page, free the rest to our slab. */
for (size_t i = 1; i < sizeof(PageBuffer) / sizeof(T); i++) {
@@ -134,6 +126,7 @@ namespace ams::kern {
}
};
class KDynamicPageManager : public KDynamicSlabHeap<impl::DynamicSlabHeapPage>{};
class KBlockInfoManager : public KDynamicSlabHeap<KBlockInfo>{};
class KMemoryBlockSlabManager : public KDynamicSlabHeap<KMemoryBlock>{};

View File

@@ -142,8 +142,6 @@ namespace ams::kern {
KMemoryPermission_KernelWrite = ams::svc::MemoryPermission_Write << KMemoryPermission_KernelShift,
KMemoryPermission_KernelExecute = ams::svc::MemoryPermission_Execute << KMemoryPermission_KernelShift,
KMemoryPermission_NotMapped = (1 << (2 * KMemoryPermission_KernelShift)),
KMemoryPermission_KernelReadWrite = KMemoryPermission_KernelRead | KMemoryPermission_KernelWrite,
KMemoryPermission_KernelReadExecute = KMemoryPermission_KernelRead | KMemoryPermission_KernelExecute,
@@ -158,13 +156,14 @@ namespace ams::kern {
};
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
return static_cast<KMemoryPermission>((perm & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((perm & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None));
return static_cast<KMemoryPermission>((perm & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((perm & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift));
}
enum KMemoryAttribute : u8 {
KMemoryAttribute_None = 0x00,
KMemoryAttribute_UserMask = 0x7F,
KMemoryAttribute_All = 0xFF,
KMemoryAttribute_Mask = 0x7F,
KMemoryAttribute_All = KMemoryAttribute_Mask,
KMemoryAttribute_DontCareMask = 0x80,
KMemoryAttribute_Locked = ams::svc::MemoryAttribute_Locked,
KMemoryAttribute_IpcLocked = ams::svc::MemoryAttribute_IpcLocked,
@@ -172,6 +171,9 @@ namespace ams::kern {
KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached,
};
static_assert((KMemoryAttribute_Mask & KMemoryAttribute_DontCareMask) == 0);
static_assert(static_cast<typename std::underlying_type<KMemoryAttribute>::type>(~(KMemoryAttribute_Mask | KMemoryAttribute_DontCareMask)) == 0);
struct KMemoryInfo {
uintptr_t address;
size_t size;
@@ -187,7 +189,7 @@ namespace ams::kern {
.addr = this->address,
.size = this->size,
.state = static_cast<ams::svc::MemoryState>(this->state & KMemoryState_Mask),
.attr = static_cast<ams::svc::MemoryAttribute>(this->attribute & KMemoryAttribute_UserMask),
.attr = static_cast<ams::svc::MemoryAttribute>(this->attribute & KMemoryAttribute_Mask),
.perm = static_cast<ams::svc::MemoryPermission>(this->perm & KMemoryPermission_UserMask),
.ipc_refcount = this->ipc_lock_count,
.device_refcount = this->device_use_count,
@@ -295,7 +297,7 @@ namespace ams::kern {
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
MESOSPHERE_ASSERT_THIS();
constexpr auto AttributeIgnoreMask = KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
constexpr auto AttributeIgnoreMask = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
return this->memory_state == s && this->perm == p && (this->attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
}

View File

@@ -94,7 +94,6 @@ namespace ams::kern {
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm);
iterator FindIterator(KProcessAddress address) const {
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));

View File

@@ -58,11 +58,11 @@ namespace ams::kern {
Impl *next;
Impl *prev;
public:
Impl() : heap(), page_reference_counts(), metadata_region(), pool(), next(), prev() { /* ... */ }
constexpr Impl() : heap(), page_reference_counts(), metadata_region(), pool(), next(), prev() { /* ... */ }
size_t Initialize(const KMemoryRegion *region, Pool pool, KVirtualAddress metadata_region, KVirtualAddress metadata_region_end);
KVirtualAddress AllocateBlock(s32 index, bool random) { return this->heap.AllocateBlock(index, random); }
KVirtualAddress AllocateBlock(s32 index) { return this->heap.AllocateBlock(index); }
void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); }
void TrackAllocationForOptimizedProcess(KVirtualAddress block, size_t num_pages);
@@ -149,10 +149,8 @@ namespace ams::kern {
return cur->GetNext();
}
}
Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool optimize, bool random);
public:
KMemoryManager()
constexpr KMemoryManager()
: pool_locks(), pool_managers_head(), pool_managers_tail(), managers(), num_managers(), optimized_process_ids(), has_optimized_process()
{
/* ... */

View File

@@ -1,267 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_system_control.hpp>
namespace ams::kern {
class KPageBitmap {
private:
class RandomBitGenerator {
private:
util::TinyMT rng;
u32 entropy;
u32 bits_available;
private:
void RefreshEntropy() {
this->entropy = rng.GenerateRandomU32();
this->bits_available = BITSIZEOF(this->entropy);
}
bool GenerateRandomBit() {
if (this->bits_available == 0) {
this->RefreshEntropy();
}
const bool rnd_bit = (this->entropy & 1) != 0;
this->entropy >>= 1;
--this->bits_available;
return rnd_bit;
}
public:
RandomBitGenerator() : rng(), entropy(), bits_available() {
this->rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
}
size_t SelectRandomBit(u64 bitmap) {
u64 selected = 0;
u64 cur_num_bits = BITSIZEOF(bitmap) / 2;
u64 cur_mask = (1ull << cur_num_bits) - 1;
while (cur_num_bits) {
const u64 low = (bitmap >> 0) & cur_mask;
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
bool choose_low;
if (high == 0) {
/* If only low val is set, choose low. */
choose_low = true;
} else if (low == 0) {
/* If only high val is set, choose high. */
choose_low = false;
} else {
/* If both are set, choose random. */
choose_low = this->GenerateRandomBit();
}
/* If we chose low, proceed with low. */
if (choose_low) {
bitmap = low;
selected += 0;
} else {
bitmap = high;
selected += cur_num_bits;
}
/* Proceed. */
cur_num_bits /= 2;
cur_mask >>= cur_num_bits;
}
return selected;
}
};
public:
static constexpr size_t MaxDepth = 4;
private:
u64 *bit_storages[MaxDepth];
RandomBitGenerator rng;
size_t num_bits;
size_t used_depths;
public:
KPageBitmap() : bit_storages(), rng(), num_bits(), used_depths() { /* ... */ }
constexpr size_t GetNumBits() const { return this->num_bits; }
constexpr s32 GetHighestDepthIndex() const { return static_cast<s32>(this->used_depths) - 1; }
u64 *Initialize(u64 *storage, size_t size) {
/* Initially, everything is un-set. */
this->num_bits = 0;
/* Calculate the needed bitmap depth. */
this->used_depths = static_cast<size_t>(GetRequiredDepth(size));
MESOSPHERE_ASSERT(this->used_depths <= MaxDepth);
/* Set the bitmap pointers. */
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
this->bit_storages[depth] = storage;
size = util::AlignUp(size, BITSIZEOF(u64)) / BITSIZEOF(u64);
storage += size;
}
return storage;
}
ssize_t FindFreeBlock(bool random) {
uintptr_t offset = 0;
s32 depth = 0;
if (random) {
do {
const u64 v = this->bit_storages[depth][offset];
if (v == 0) {
/* If depth is bigger than zero, then a previous level indicated a block was free. */
MESOSPHERE_ASSERT(depth == 0);
return -1;
}
offset = offset * BITSIZEOF(u64) + this->rng.SelectRandomBit(v);
++depth;
} while (depth < static_cast<s32>(this->used_depths));
} else {
do {
const u64 v = this->bit_storages[depth][offset];
if (v == 0) {
/* If depth is bigger than zero, then a previous level indicated a block was free. */
MESOSPHERE_ASSERT(depth == 0);
return -1;
}
offset = offset * BITSIZEOF(u64) + __builtin_ctzll(v);
++depth;
} while (depth < static_cast<s32>(this->used_depths));
}
return static_cast<ssize_t>(offset);
}
void SetBit(size_t offset) {
this->SetBit(this->GetHighestDepthIndex(), offset);
this->num_bits++;
}
void ClearBit(size_t offset) {
this->ClearBit(this->GetHighestDepthIndex(), offset);
this->num_bits--;
}
bool ClearRange(size_t offset, size_t count) {
s32 depth = this->GetHighestDepthIndex();
u64 *bits = this->bit_storages[depth];
size_t bit_ind = offset / BITSIZEOF(u64);
if (AMS_LIKELY(count < BITSIZEOF(u64))) {
const size_t shift = offset % BITSIZEOF(u64);
MESOSPHERE_ASSERT(shift + count <= BITSIZEOF(u64));
/* Check that all the bits are set. */
const u64 mask = ((u64(1) << count) - 1) << shift;
u64 v = bits[bit_ind];
if ((v & mask) != mask) {
return false;
}
/* Clear the bits. */
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
this->ClearBit(depth - 1, bit_ind);
}
} else {
MESOSPHERE_ASSERT(offset % BITSIZEOF(u64) == 0);
MESOSPHERE_ASSERT(count % BITSIZEOF(u64) == 0);
/* Check that all the bits are set. */
size_t remaining = count;
size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= BITSIZEOF(u64);
} while (remaining > 0);
/* Clear the bits. */
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
this->ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= BITSIZEOF(u64);
} while (remaining > 0);
}
this->num_bits -= count;
return true;
}
private:
void SetBit(s32 depth, size_t offset) {
while (depth >= 0) {
size_t ind = offset / BITSIZEOF(u64);
size_t which = offset % BITSIZEOF(u64);
const u64 mask = u64(1) << which;
u64 *bit = std::addressof(this->bit_storages[depth][ind]);
u64 v = *bit;
MESOSPHERE_ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
void ClearBit(s32 depth, size_t offset) {
while (depth >= 0) {
size_t ind = offset / BITSIZEOF(u64);
size_t which = offset % BITSIZEOF(u64);
const u64 mask = u64(1) << which;
u64 *bit = std::addressof(this->bit_storages[depth][ind]);
u64 v = *bit;
MESOSPHERE_ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= BITSIZEOF(u64);
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr size_t CalculateMetadataOverheadSize(size_t region_size) {
size_t overhead_bits = 0;
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
region_size = util::AlignUp(region_size, BITSIZEOF(u64)) / BITSIZEOF(u64);
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
}

View File

@@ -15,7 +15,6 @@
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_page_bitmap.hpp>
namespace ams::kern {
@@ -53,13 +52,178 @@ namespace ams::kern {
private:
class Block {
private:
KPageBitmap bitmap;
class Bitmap {
public:
static constexpr size_t MaxDepth = 4;
private:
u64 *bit_storages[MaxDepth];
size_t num_bits;
size_t used_depths;
public:
constexpr Bitmap() : bit_storages(), num_bits(), used_depths() { /* ... */ }
constexpr size_t GetNumBits() const { return this->num_bits; }
constexpr s32 GetHighestDepthIndex() const { return static_cast<s32>(this->used_depths) - 1; }
u64 *Initialize(u64 *storage, size_t size) {
/* Initially, everything is un-set. */
this->num_bits = 0;
/* Calculate the needed bitmap depth. */
this->used_depths = static_cast<size_t>(GetRequiredDepth(size));
MESOSPHERE_ASSERT(this->used_depths <= MaxDepth);
/* Set the bitmap pointers. */
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
this->bit_storages[depth] = storage;
size = util::AlignUp(size, BITSIZEOF(u64)) / BITSIZEOF(u64);
storage += size;
}
return storage;
}
ssize_t FindFreeBlock() const {
uintptr_t offset = 0;
s32 depth = 0;
do {
const u64 v = this->bit_storages[depth][offset];
if (v == 0) {
/* If depth is bigger than zero, then a previous level indicated a block was free. */
MESOSPHERE_ASSERT(depth == 0);
return -1;
}
offset = offset * BITSIZEOF(u64) + __builtin_ctzll(v);
++depth;
} while (depth < static_cast<s32>(this->used_depths));
return static_cast<ssize_t>(offset);
}
void SetBit(size_t offset) {
this->SetBit(this->GetHighestDepthIndex(), offset);
this->num_bits++;
}
void ClearBit(size_t offset) {
this->ClearBit(this->GetHighestDepthIndex(), offset);
this->num_bits--;
}
bool ClearRange(size_t offset, size_t count) {
s32 depth = this->GetHighestDepthIndex();
u64 *bits = this->bit_storages[depth];
size_t bit_ind = offset / BITSIZEOF(u64);
if (AMS_LIKELY(count < BITSIZEOF(u64))) {
const size_t shift = offset % BITSIZEOF(u64);
MESOSPHERE_ASSERT(shift + count <= BITSIZEOF(u64));
/* Check that all the bits are set. */
const u64 mask = ((u64(1) << count) - 1) << shift;
u64 v = bits[bit_ind];
if ((v & mask) != mask) {
return false;
}
/* Clear the bits. */
v &= ~mask;
bits[bit_ind] = v;
if (v == 0) {
this->ClearBit(depth - 1, bit_ind);
}
} else {
MESOSPHERE_ASSERT(offset % BITSIZEOF(u64) == 0);
MESOSPHERE_ASSERT(count % BITSIZEOF(u64) == 0);
/* Check that all the bits are set. */
size_t remaining = count;
size_t i = 0;
do {
if (bits[bit_ind + i++] != ~u64(0)) {
return false;
}
remaining -= BITSIZEOF(u64);
} while (remaining > 0);
/* Clear the bits. */
remaining = count;
i = 0;
do {
bits[bit_ind + i] = 0;
this->ClearBit(depth - 1, bit_ind + i);
i++;
remaining -= BITSIZEOF(u64);
} while (remaining > 0);
}
this->num_bits -= count;
return true;
}
private:
void SetBit(s32 depth, size_t offset) {
while (depth >= 0) {
size_t ind = offset / BITSIZEOF(u64);
size_t which = offset % BITSIZEOF(u64);
const u64 mask = u64(1) << which;
u64 *bit = std::addressof(this->bit_storages[depth][ind]);
u64 v = *bit;
MESOSPHERE_ASSERT((v & mask) == 0);
*bit = v | mask;
if (v) {
break;
}
offset = ind;
depth--;
}
}
void ClearBit(s32 depth, size_t offset) {
while (depth >= 0) {
size_t ind = offset / BITSIZEOF(u64);
size_t which = offset % BITSIZEOF(u64);
const u64 mask = u64(1) << which;
u64 *bit = std::addressof(this->bit_storages[depth][ind]);
u64 v = *bit;
MESOSPHERE_ASSERT((v & mask) != 0);
v &= ~mask;
*bit = v;
if (v) {
break;
}
offset = ind;
depth--;
}
}
private:
static constexpr s32 GetRequiredDepth(size_t region_size) {
s32 depth = 0;
while (true) {
region_size /= BITSIZEOF(u64);
depth++;
if (region_size == 0) {
return depth;
}
}
}
public:
static constexpr size_t CalculateMetadataOverheadSize(size_t region_size) {
size_t overhead_bits = 0;
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
region_size = util::AlignUp(region_size, BITSIZEOF(u64)) / BITSIZEOF(u64);
overhead_bits += region_size;
}
return overhead_bits * sizeof(u64);
}
};
private:
Bitmap bitmap;
KVirtualAddress heap_address;
uintptr_t end_offset;
size_t block_shift;
size_t next_block_shift;
public:
Block() : bitmap(), heap_address(), end_offset(), block_shift(), next_block_shift() { /* ... */ }
constexpr Block() : bitmap(), heap_address(), end_offset(), block_shift(), next_block_shift() { /* ... */ }
constexpr size_t GetShift() const { return this->block_shift; }
constexpr size_t GetNextShift() const { return this->next_block_shift; }
@@ -102,9 +266,9 @@ namespace ams::kern {
return Null<KVirtualAddress>;
}
KVirtualAddress PopBlock(bool random) {
KVirtualAddress PopBlock() {
/* Find a free block. */
ssize_t soffset = this->bitmap.FindFreeBlock(random);
ssize_t soffset = this->bitmap.FindFreeBlock();
if (soffset < 0) {
return Null<KVirtualAddress>;
}
@@ -119,7 +283,7 @@ namespace ams::kern {
const size_t cur_block_size = (u64(1) << cur_block_shift);
const size_t next_block_size = (u64(1) << next_block_shift);
const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
return KPageBitmap::CalculateMetadataOverheadSize((align * 2 + util::AlignUp(region_size, align)) / cur_block_size);
return Bitmap::CalculateMetadataOverheadSize((align * 2 + util::AlignUp(region_size, align)) / cur_block_size);
}
};
private:
@@ -134,7 +298,7 @@ namespace ams::kern {
void FreeBlock(KVirtualAddress block, s32 index);
public:
KPageHeap() : heap_address(), heap_size(), used_size(), num_blocks(), blocks() { /* ... */ }
constexpr KPageHeap() : heap_address(), heap_size(), used_size(), num_blocks(), blocks() { /* ... */ }
constexpr KVirtualAddress GetAddress() const { return this->heap_address; }
constexpr size_t GetSize() const { return this->heap_size; }
@@ -149,7 +313,7 @@ namespace ams::kern {
this->used_size = this->heap_size - (this->GetNumFreePages() * PageSize);
}
KVirtualAddress AllocateBlock(s32 index, bool random);
KVirtualAddress AllocateBlock(s32 index);
void Free(KVirtualAddress addr, size_t num_pages);
private:
static size_t CalculateMetadataOverheadSize(size_t region_size, const size_t *block_shifts, size_t num_block_shifts);

View File

@@ -91,7 +91,7 @@ namespace ams::kern {
};
static_assert(std::is_trivially_destructible<PageLinkedList>::value);
static constexpr u32 DefaultMemoryIgnoreAttr = KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
static constexpr u32 DefaultMemoryIgnoreAttr = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
static constexpr size_t GetAddressSpaceWidth(ams::svc::CreateProcessFlag as_type) {
switch (static_cast<ams::svc::CreateProcessFlag>(as_type & ams::svc::CreateProcessFlag_AddressSpaceMask)) {
@@ -135,7 +135,6 @@ namespace ams::kern {
KProcessAddress code_region_end;
size_t max_heap_size;
size_t max_physical_memory_size;
size_t mapped_unsafe_physical_memory;
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
KPageTableImpl impl;
@@ -157,9 +156,9 @@ namespace ams::kern {
address_space_start(), address_space_end(), heap_region_start(), heap_region_end(), current_heap_end(),
alias_region_start(), alias_region_end(), stack_region_start(), stack_region_end(), kernel_map_region_start(),
kernel_map_region_end(), alias_code_region_start(), alias_code_region_end(), code_region_start(), code_region_end(),
max_heap_size(), max_physical_memory_size(),mapped_unsafe_physical_memory(), general_lock(), map_physical_memory_lock(),
impl(), memory_block_manager(), allocate_option(), address_space_width(), is_kernel(), enable_aslr(), memory_block_slab_manager(),
block_info_manager(), cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(),
max_heap_size(), max_physical_memory_size(), general_lock(), map_physical_memory_lock(), impl(), memory_block_manager(),
allocate_option(), address_space_width(), is_kernel(), enable_aslr(), memory_block_slab_manager(), block_info_manager(),
cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(),
heap_fill_value(), ipc_fill_value(), stack_fill_value()
{
/* ... */

View File

@@ -57,13 +57,13 @@ namespace ams::kern {
return std::addressof(this->ref_counts[(addr - this->GetAddress()) / PageSize]);
}
public:
void Initialize(KDynamicPageManager *page_allocator, RefCount *rc) {
BaseHeap::Initialize(page_allocator);
void Initialize(KDynamicPageManager *next_allocator, RefCount *rc) {
BaseHeap::Initialize(next_allocator);
this->Initialize(rc);
}
void Initialize(KDynamicPageManager *page_allocator, size_t object_count, RefCount *rc) {
BaseHeap::Initialize(page_allocator, object_count);
void Initialize(KVirtualAddress memory, size_t sz, RefCount *rc) {
BaseHeap::Initialize(memory, sz);
this->Initialize(rc);
}
@@ -72,10 +72,6 @@ namespace ams::kern {
}
void Free(KVirtualAddress addr) {
/* Ensure all pages in the heap are zero. */
cpu::ClearPageToZero(GetVoidPointer(addr));
/* Free the page. */
BaseHeap::Free(GetPointer<impl::PageTablePage>(addr));
}

View File

@@ -89,7 +89,6 @@ namespace ams::kern {
u32 version{};
KHandleTable handle_table{};
KProcessAddress plr_address{};
void *plr_heap_address{};
KThread *exception_thread{};
ThreadList thread_list{};
SharedMemoryInfoList shared_memory_list{};
@@ -119,7 +118,7 @@ namespace ams::kern {
private:
Result Initialize(const ams::svc::CreateProcessParameter &params);
public:
KProcess() { /* ... */ }
constexpr 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);

View File

@@ -333,13 +333,6 @@ namespace ams::kern::arch::arm64::cpu {
return PerformCacheOperationBySetWayLocal<true>(FlushDataCacheLineBySetWayImpl);
}
void StoreEntireCacheForInit() {
PerformCacheOperationBySetWayLocal<true>(StoreDataCacheLineBySetWayImpl);
PerformCacheOperationBySetWayShared<true>(StoreDataCacheLineBySetWayImpl);
DataSynchronizationBarrierInnerShareable();
InvalidateEntireInstructionCache();
}
void FlushEntireDataCache() {
return PerformCacheOperationBySetWayShared<false>(FlushDataCacheLineBySetWayImpl);
}

View File

@@ -292,7 +292,7 @@ namespace ams::kern::arch::arm64 {
/* Set the entry. */
l2_phys = GetPageTablePhysicalAddress(l2_virt);
PteDataSynchronizationBarrier();
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, l2_phys, this->IsKernel(), true);
*l1_entry = L1PageTableEntry(l2_phys, this->IsKernel(), true);
PteDataSynchronizationBarrier();
l2_allocated = true;
} else {
@@ -319,7 +319,7 @@ namespace ams::kern::arch::arm64 {
/* Set the entry. */
l3_phys = GetPageTablePhysicalAddress(l3_virt);
PteDataSynchronizationBarrier();
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, l3_phys, this->IsKernel(), true);
*l2_entry = L2PageTableEntry(l3_phys, this->IsKernel(), true);
PteDataSynchronizationBarrier();
l2_open_count++;
} else {
@@ -329,7 +329,7 @@ namespace ams::kern::arch::arm64 {
MESOSPHERE_ASSERT(l3_virt != Null<KVirtualAddress>);
/* Map the page. */
*impl.GetL3EntryFromTable(l3_virt, virt_addr) = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), false);
*impl.GetL3EntryFromTable(l3_virt, virt_addr) = L3PageTableEntry(phys_addr, entry_template, false);
l3_open_count++;
virt_addr += PageSize;
phys_addr += PageSize;
@@ -706,8 +706,6 @@ namespace ams::kern::arch::arm64 {
/* If there's no L1 table, don't bother. */
L1PageTableEntry *l1_entry = impl.GetL1Entry(virt_addr);
if (!l1_entry->IsTable()) {
/* Ensure the table is not corrupted. */
MESOSPHERE_ABORT_UNLESS(l1_entry->IsBlock() || l1_entry->IsEmpty());
return merged;
}
@@ -728,7 +726,7 @@ namespace ams::kern::arch::arm64 {
/* Validate that we can merge. */
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
if (!impl.GetL3Entry(l2_entry, virt_addr + L3BlockSize * i)->Is(entry_template | GetInteger(phys_addr + L3BlockSize * i) | PageTableEntry::Type_L3Block)) {
if (!impl.GetL3Entry(l2_entry, virt_addr + L3BlockSize * i)->Is(entry_template | GetInteger(phys_addr + PageSize * i) | PageTableEntry::Type_L3Block)) {
return merged;
}
}
@@ -750,14 +748,14 @@ namespace ams::kern::arch::arm64 {
/* Validate that we can merge. */
for (size_t i = 0; i < L2BlockSize / L3ContiguousBlockSize; i++) {
if (!impl.GetL3Entry(l2_entry, virt_addr + L3ContiguousBlockSize * i)->Is(entry_template | GetInteger(phys_addr + L3ContiguousBlockSize * i) | PageTableEntry::ContigType_Contiguous | PageTableEntry::Type_L3Block)) {
if (!impl.GetL3Entry(l2_entry, virt_addr + L3BlockSize * i)->Is(entry_template | GetInteger(phys_addr + L3ContiguousBlockSize * i) | PageTableEntry::ContigType_Contiguous)) {
return merged;
}
}
/* Merge! */
PteDataSynchronizationBarrier();
*l2_entry = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), false);
*l2_entry = L2PageTableEntry(phys_addr, entry_template, false);
/* Note that we updated. */
this->NoteUpdated();
@@ -767,67 +765,61 @@ namespace ams::kern::arch::arm64 {
KVirtualAddress l3_table = util::AlignDown(reinterpret_cast<uintptr_t>(l3_entry), PageSize);
if (this->GetPageTableManager().IsInPageTableHeap(l3_table)) {
this->GetPageTableManager().Close(l3_table, L2BlockSize / L3BlockSize);
ClearPageTable(l3_table);
this->FreePageTable(page_list, l3_table);
}
}
if (l2_entry->IsBlock()) {
/* If it's not contiguous, try to make it so. */
if (!l2_entry->IsContiguous()) {
virt_addr = util::AlignDown(GetInteger(virt_addr), L2ContiguousBlockSize);
KPhysicalAddress phys_addr = util::AlignDown(GetInteger(l2_entry->GetBlock()), L2ContiguousBlockSize);
const u64 entry_template = l2_entry->GetEntryTemplate();
/* If the l2 entry is not a block or we can't make it contiguous, we're done. */
if (!l2_entry->IsBlock() || !l2_entry->IsContiguousAllowed()) {
return merged;
}
/* Validate that we can merge. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
if (!impl.GetL2Entry(l1_entry, virt_addr + L2BlockSize * i)->Is(entry_template | GetInteger(phys_addr + PageSize * i) | PageTableEntry::Type_L2Block)) {
return merged;
}
}
/* If it's not contiguous, try to make it so. */
if (!l2_entry->IsContiguous()) {
virt_addr = util::AlignDown(GetInteger(virt_addr), L2ContiguousBlockSize);
KPhysicalAddress phys_addr = util::AlignDown(GetInteger(l2_entry->GetBlock()), L2ContiguousBlockSize);
/* Merge! */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
impl.GetL2Entry(l1_entry, virt_addr + L2BlockSize * i)->SetContiguous(true);
}
/* Note that we updated. */
this->NoteUpdated();
merged = true;
}
/* We might be able to upgrade a contiguous set of L2 entries into an L1 block. */
virt_addr = util::AlignDown(GetInteger(virt_addr), L1BlockSize);
KPhysicalAddress phys_addr = util::AlignDown(GetInteger(l2_entry->GetBlock()), L1BlockSize);
const u64 entry_template = l2_entry->GetEntryTemplate();
/* Validate that we can merge. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
if (!impl.GetL2Entry(l1_entry, virt_addr + L2BlockSize * i)->Is(entry_template | GetInteger(phys_addr + L2BlockSize * i) | PageTableEntry::Type_L2Block)) {
for (size_t i = 0; i < L1BlockSize / L2ContiguousBlockSize; i++) {
if (!impl.GetL2Entry(l1_entry, virt_addr + L3BlockSize * i)->Is(entry_template | GetInteger(phys_addr + L2ContiguousBlockSize * i) | PageTableEntry::ContigType_Contiguous)) {
return merged;
}
}
/* Merge! */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
impl.GetL2Entry(l1_entry, virt_addr + L2BlockSize * i)->SetContiguous(true);
}
PteDataSynchronizationBarrier();
*l1_entry = L1PageTableEntry(phys_addr, entry_template, false);
/* Note that we updated. */
this->NoteUpdated();
merged = true;
}
/* We might be able to upgrade a contiguous set of L2 entries into an L1 block. */
virt_addr = util::AlignDown(GetInteger(virt_addr), L1BlockSize);
KPhysicalAddress phys_addr = util::AlignDown(GetInteger(l2_entry->GetBlock()), L1BlockSize);
const u64 entry_template = l2_entry->GetEntryTemplate();
/* Validate that we can merge. */
for (size_t i = 0; i < L1BlockSize / L2ContiguousBlockSize; i++) {
if (!impl.GetL2Entry(l1_entry, virt_addr + L2ContiguousBlockSize * i)->Is(entry_template | GetInteger(phys_addr + L2ContiguousBlockSize * i) | PageTableEntry::ContigType_Contiguous | PageTableEntry::Type_L2Block)) {
return merged;
/* Free the L2 table. */
KVirtualAddress l2_table = util::AlignDown(reinterpret_cast<uintptr_t>(l2_entry), PageSize);
if (this->GetPageTableManager().IsInPageTableHeap(l2_table)) {
this->GetPageTableManager().Close(l2_table, L1BlockSize / L2BlockSize);
this->FreePageTable(page_list, l2_table);
}
}
/* Merge! */
PteDataSynchronizationBarrier();
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), false);
/* Note that we updated. */
this->NoteUpdated();
merged = true;
/* Free the L2 table. */
KVirtualAddress l2_table = util::AlignDown(reinterpret_cast<uintptr_t>(l2_entry), PageSize);
if (this->GetPageTableManager().IsInPageTableHeap(l2_table)) {
this->GetPageTableManager().Close(l2_table, L1BlockSize / L2BlockSize);
ClearPageTable(l2_table);
this->FreePageTable(page_list, l2_table);
}
return merged;
}
@@ -854,7 +846,7 @@ namespace ams::kern::arch::arm64 {
/* Set the entries in the L2 table. */
const u64 entry_template = l1_entry->GetEntryTemplate();
for (size_t i = 0; i < L1BlockSize / L2BlockSize; i++) {
*(impl.GetL2EntryFromTable(l2_table, block_virt_addr + L2BlockSize * i)) = L2PageTableEntry(PageTableEntry::BlockTag{}, block_phys_addr + L2BlockSize * i, PageTableEntry(entry_template), true);
*(impl.GetL2EntryFromTable(l2_table, block_virt_addr + L2BlockSize * i)) = L2PageTableEntry(block_phys_addr + L2BlockSize * i, entry_template, true);
}
/* Open references to the L2 table. */
@@ -862,12 +854,11 @@ namespace ams::kern::arch::arm64 {
/* Replace the L1 entry with one to the new table. */
PteDataSynchronizationBarrier();
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, l2_phys, this->IsKernel(), true);
*l1_entry = L1PageTableEntry(l2_phys, this->IsKernel(), true);
this->NoteUpdated();
}
/* If we don't have an l1 table, we're done. */
MESOSPHERE_ABORT_UNLESS(l1_entry->IsTable() || l1_entry->IsEmpty());
R_SUCCEED_IF(!l1_entry->IsTable());
/* We want to separate L2 contiguous blocks into L2 blocks, so check that our size permits that. */
@@ -901,7 +892,7 @@ namespace ams::kern::arch::arm64 {
/* Set the entries in the L3 table. */
const u64 entry_template = l2_entry->GetEntryTemplate();
for (size_t i = 0; i < L2BlockSize / L3BlockSize; i++) {
*(impl.GetL3EntryFromTable(l3_table, block_virt_addr + L3BlockSize * i)) = L3PageTableEntry(PageTableEntry::BlockTag{}, block_phys_addr + L3BlockSize * i, PageTableEntry(entry_template), true);
*(impl.GetL3EntryFromTable(l3_table, block_virt_addr + L3BlockSize * i)) = L3PageTableEntry(block_phys_addr + L3BlockSize * i, entry_template, true);
}
/* Open references to the L3 table. */
@@ -909,12 +900,11 @@ namespace ams::kern::arch::arm64 {
/* Replace the L2 entry with one to the new table. */
PteDataSynchronizationBarrier();
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, l3_phys, this->IsKernel(), true);
*l2_entry = L2PageTableEntry(l3_phys, this->IsKernel(), true);
this->NoteUpdated();
}
/* If we don't have an L3 table, we're done. */
MESOSPHERE_ABORT_UNLESS(l2_entry->IsTable() || l2_entry->IsEmpty());
R_SUCCEED_IF(!l2_entry->IsTable());
/* We want to separate L3 contiguous blocks into L2 blocks, so check that our size permits that. */
@@ -950,6 +940,8 @@ namespace ams::kern::arch::arm64 {
Result KPageTable::ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll) {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
auto &impl = this->GetImpl();
/* Separate pages before we change permissions. */
const size_t size = num_pages * PageSize;
R_TRY(this->SeparatePages(virt_addr, std::min(GetInteger(virt_addr) & -GetInteger(virt_addr), size), page_list, reuse_ll));
@@ -962,163 +954,117 @@ namespace ams::kern::arch::arm64 {
merge_guard.Cancel();
}
/* ===================================================== */
/* Cache initial addresses for use on cleanup. */
const KProcessAddress orig_virt_addr = virt_addr;
size_t remaining_pages = num_pages;
/* Define a helper function which will apply our template to entries. */
/* Begin traversal. */
TraversalContext context;
TraversalEntry next_entry;
MESOSPHERE_ABORT_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr));
enum ApplyOption : u32 {
ApplyOption_None = 0,
ApplyOption_FlushDataCache = (1u << 0),
ApplyOption_MergeMappings = (1u << 1),
};
/* Continue changing properties until we've changed them for all pages. */
while (remaining_pages > 0) {
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(next_entry.phys_addr), next_entry.block_size));
MESOSPHERE_ABORT_UNLESS(next_entry.block_size <= remaining_pages * PageSize);
auto ApplyEntryTemplate = [this, virt_addr, num_pages, page_list](PageTableEntry entry_template, u32 apply_option) -> void {
/* Create work variables for us to use. */
KProcessAddress cur_virt_addr = virt_addr;
size_t remaining_pages = num_pages;
auto &impl = this->GetImpl();
/* Begin traversal. */
TraversalContext context;
TraversalEntry next_entry;
MESOSPHERE_ABORT_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), cur_virt_addr));
/* Continue changing properties until we've changed them for all pages. */
while (remaining_pages > 0) {
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(next_entry.phys_addr), next_entry.block_size));
MESOSPHERE_ABORT_UNLESS(next_entry.block_size <= remaining_pages * PageSize);
/* If we should flush entries, do so. */
if ((apply_option & ApplyOption_FlushDataCache) != 0) {
if (IsHeapPhysicalAddress(next_entry.phys_addr)) {
cpu::FlushDataCache(GetVoidPointer(GetHeapVirtualAddress(next_entry.phys_addr)), next_entry.block_size);
}
}
/* Apply the entry template. */
L1PageTableEntry *l1_entry = impl.GetL1Entry(cur_virt_addr);
switch (next_entry.block_size) {
case L1BlockSize:
{
/* Write the updated entry. */
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, next_entry.phys_addr, entry_template, false);
}
break;
case L2ContiguousBlockSize:
case L2BlockSize:
{
/* Get the number of L2 blocks. */
const size_t num_l2_blocks = next_entry.block_size / L2BlockSize;
/* Get the L2 entry. */
KPhysicalAddress l2_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l1_entry->GetTable(l2_phys));
const KVirtualAddress l2_virt = GetPageTableVirtualAddress(l2_phys);
/* Write the updated entry. */
const bool contig = next_entry.block_size == L2ContiguousBlockSize;
for (size_t i = 0; i < num_l2_blocks; i++) {
*impl.GetL2EntryFromTable(l2_virt, cur_virt_addr + L2BlockSize * i) = L2PageTableEntry(PageTableEntry::BlockTag{}, next_entry.phys_addr + L2BlockSize * i, entry_template, contig);
L1PageTableEntry *l1_entry = impl.GetL1Entry(virt_addr);
switch (next_entry.block_size) {
case L1BlockSize:
{
/* Clear the entry, if we should. */
if (refresh_mapping) {
*l1_entry = InvalidL1PageTableEntry;
this->NoteUpdated();
if (IsHeapPhysicalAddress(next_entry.phys_addr)) {
cpu::FlushDataCache(GetVoidPointer(GetHeapVirtualAddress(next_entry.phys_addr)), L1BlockSize);
}
}
break;
case L3ContiguousBlockSize:
case L3BlockSize:
{
/* Get the number of L3 blocks. */
const size_t num_l3_blocks = next_entry.block_size / L3BlockSize;
/* Get the L2 entry. */
KPhysicalAddress l2_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l1_entry->GetTable(l2_phys));
const KVirtualAddress l2_virt = GetPageTableVirtualAddress(l2_phys);
L2PageTableEntry *l2_entry = impl.GetL2EntryFromTable(l2_virt, cur_virt_addr);
/* Get the L3 entry. */
KPhysicalAddress l3_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l2_entry->GetTable(l3_phys));
const KVirtualAddress l3_virt = GetPageTableVirtualAddress(l3_phys);
/* Write the updated entry. */
const bool contig = next_entry.block_size == L3ContiguousBlockSize;
for (size_t i = 0; i < num_l3_blocks; i++) {
*impl.GetL3EntryFromTable(l3_virt, cur_virt_addr + L3BlockSize * i) = L3PageTableEntry(PageTableEntry::BlockTag{}, next_entry.phys_addr + L3BlockSize * i, entry_template, contig);
}
}
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* If our option asks us to, try to merge mappings. */
bool merge = ((apply_option & ApplyOption_MergeMappings) != 0) && next_entry.block_size < L1BlockSize;
if (merge) {
const size_t larger_align = GetLargerAlignment(next_entry.block_size);
if (util::IsAligned(GetInteger(cur_virt_addr) + next_entry.block_size, larger_align)) {
const uintptr_t aligned_start = util::AlignDown(GetInteger(cur_virt_addr), larger_align);
if (virt_addr <= aligned_start && aligned_start + larger_align - 1 < GetInteger(virt_addr) + (num_pages * PageSize) - 1) {
merge = this->MergePages(cur_virt_addr, page_list);
} else {
merge = false;
}
} else {
merge = false;
/* Write the updated entry. */
*l1_entry = L1PageTableEntry(next_entry.phys_addr, entry_template, false);
}
}
/* If we merged, correct the traversal to a sane state. */
if (merge) {
/* NOTE: Nintendo does not verify the result of this BeginTraversal call. */
MESOSPHERE_ABORT_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), cur_virt_addr));
/* The actual size needs to not take into account the portion of the block before our virtual address. */
const size_t actual_size = next_entry.block_size - (GetInteger(next_entry.phys_addr) & (next_entry.block_size - 1));
remaining_pages -= std::min(remaining_pages, actual_size / PageSize);
cur_virt_addr += actual_size;
} else {
/* If we didn't merge, just advance. */
remaining_pages -= next_entry.block_size / PageSize;
cur_virt_addr += next_entry.block_size;
}
/* Continue our traversal. */
if (remaining_pages == 0) {
break;
}
MESOSPHERE_ABORT_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
}
};
case L2ContiguousBlockSize:
case L2BlockSize:
{
/* Get the number of L2 blocks. */
const size_t num_l2_blocks = next_entry.block_size / L2BlockSize;
/* Get the L2 entry. */
KPhysicalAddress l2_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l1_entry->GetTable(l2_phys));
const KVirtualAddress l2_virt = GetPageTableVirtualAddress(l2_phys);
/* ===================================================== */
/* Clear the entry, if we should. */
if (refresh_mapping) {
for (size_t i = 0; i < num_l2_blocks; i++) {
*impl.GetL2EntryFromTable(l2_virt, virt_addr + L2BlockSize * i) = InvalidL2PageTableEntry;
}
this->NoteUpdated();
if (IsHeapPhysicalAddress(next_entry.phys_addr)) {
cpu::FlushDataCache(GetVoidPointer(GetHeapVirtualAddress(next_entry.phys_addr)), next_entry.block_size);
}
}
/* If we don't need to refresh the pages, we can just apply the mappings. */
if (!refresh_mapping) {
ApplyEntryTemplate(entry_template, ApplyOption_None);
this->NoteUpdated();
} else {
/* We need to refresh the mappings. */
/* First, apply the changes without the mapped bit. This will cause all entries to page fault if accessed. */
{
PageTableEntry unmapped_template = entry_template;
unmapped_template.SetMapped(false);
ApplyEntryTemplate(unmapped_template, ApplyOption_MergeMappings);
this->NoteUpdated();
/* Write the updated entry. */
const bool contig = next_entry.block_size == L2ContiguousBlockSize;
for (size_t i = 0; i < num_l2_blocks; i++) {
*impl.GetL2EntryFromTable(l2_virt, virt_addr + L2BlockSize * i) = L2PageTableEntry(next_entry.phys_addr + L2BlockSize * i, entry_template, contig);
}
}
break;
case L3ContiguousBlockSize:
case L3BlockSize:
{
/* Get the number of L3 blocks. */
const size_t num_l3_blocks = next_entry.block_size / L3BlockSize;
/* Get the L2 entry. */
KPhysicalAddress l2_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l1_entry->GetTable(l2_phys));
const KVirtualAddress l2_virt = GetPageTableVirtualAddress(l2_phys);
L2PageTableEntry *l2_entry = impl.GetL2EntryFromTable(l2_virt, virt_addr);
/* Get the L3 entry. */
KPhysicalAddress l3_phys = Null<KPhysicalAddress>;
MESOSPHERE_ABORT_UNLESS(l2_entry->GetTable(l3_phys));
const KVirtualAddress l3_virt = GetPageTableVirtualAddress(l3_phys);
/* Clear the entry, if we should. */
if (refresh_mapping) {
for (size_t i = 0; i < num_l3_blocks; i++) {
*impl.GetL3EntryFromTable(l3_virt, virt_addr + L3BlockSize * i) = InvalidL3PageTableEntry;
}
this->NoteUpdated();
if (IsHeapPhysicalAddress(next_entry.phys_addr)) {
cpu::FlushDataCache(GetVoidPointer(GetHeapVirtualAddress(next_entry.phys_addr)), next_entry.block_size);
}
}
/* Write the updated entry. */
const bool contig = next_entry.block_size == L3ContiguousBlockSize;
for (size_t i = 0; i < num_l3_blocks; i++) {
*impl.GetL3EntryFromTable(l3_virt, virt_addr + L3BlockSize * i) = L3PageTableEntry(next_entry.phys_addr + L3BlockSize * i, entry_template, contig);
}
}
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Next, take and immediately release the scheduler lock. This will force a reschedule. */
{
KScopedSchedulerLock sl;
/* Advance. */
virt_addr += next_entry.block_size;
remaining_pages -= next_entry.block_size / PageSize;
if (remaining_pages == 0) {
break;
}
/* Finally, apply the changes as directed, flushing the mappings before they're applied. */
ApplyEntryTemplate(entry_template, ApplyOption_FlushDataCache);
MESOSPHERE_ABORT_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
}
/* We've succeeded, now perform what coalescing we can. */
this->MergePages(virt_addr, page_list);
this->MergePages(orig_virt_addr, page_list);
if (num_pages > 1) {
this->MergePages(virt_addr + (num_pages - 1) * PageSize, page_list);
this->MergePages(orig_virt_addr + (num_pages - 1) * PageSize, page_list);
}
return ResultSuccess();

View File

@@ -81,15 +81,6 @@ namespace ams::kern::board::nintendo::nx {
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() {
return g_random_generator.GenerateRandomU64();
}
@@ -313,20 +304,16 @@ namespace ams::kern::board::nintendo::nx {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
EnsureRandomGeneratorInitialized();
if (AMS_UNLIKELY(!g_initialized_random_generator)) {
u64 seed;
GenerateRandomBytes(&seed, sizeof(seed));
g_random_generator.Initialize(reinterpret_cast<u32*>(&seed), sizeof(seed) / sizeof(u32));
g_initialized_random_generator = true;
}
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
}
u64 KSystemControl::GenerateRandomU64() {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(g_random_lock);
EnsureRandomGeneratorInitialized();
return GenerateRandomU64FromGenerator();
}
void KSystemControl::SleepSystem() {
MESOSPHERE_LOG("SleepSystem() was called\n");
KSleepManager::SleepSystem();

View File

@@ -197,7 +197,6 @@ namespace ams::kern {
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
}
it++;
}
/* Find the iterator now that we've updated. */
@@ -228,86 +227,6 @@ namespace ams::kern {
}
}
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm) {
/* Ensure for auditing that we never end up with an invalid tree. */
KScopedMemoryBlockManagerAuditor auditor(this);
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
KProcessAddress cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
iterator prev = it, next = it;
bool check_coalesce_prev = false, check_coalesce_next = false;
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
/* If we need to, create a new block before and insert it. */
if (cur_info.address != GetInteger(cur_address)) {
KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = this->memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
} else if (cur_address == address && cur_address != this->start_address) {
/* If there's a previous, we should check for coalescing. */
check_coalesce_prev = true;
prev--;
} else if (cur_info.GetSize() > remaining_size) {
/* If we need to, create a new block after and insert it. */
KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = this->memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} else if (cur_info.GetSize() == remaining_size) {
/* Otherwise if we can map precisely, we may need to check for coalescing against next block. */
next = it;
++next;
if (next != this->memory_block_tree.end()) {
check_coalesce_next = true;
}
}
/* Call the locked update function. */
(std::addressof(*it)->*lock_func)(perm);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
it++;
}
/* If we should try to coalesce prev, do so. */
if (check_coalesce_prev) {
it = prev;
it++;
if (prev->HasSameProperties(*it)) {
KMemoryBlock *block = std::addressof(*it);
const size_t pages = it->GetNumPages();
this->memory_block_tree.erase(it);
allocator->Free(block);
prev->Add(pages);
}
}
/* If we should try to coalesce next, do so. */
if (check_coalesce_next) {
it = next;
it--;
if (it->HasSameProperties(*next)) {
KMemoryBlock *block = std::addressof(*next);
const size_t pages = next->GetNumPages();
this->memory_block_tree.erase(next);
allocator->Free(block);
it->Add(pages);
}
}
}
/* Debug. */
bool KMemoryBlockManager::CheckState() const {
/* If we fail, we should dump blocks. */

View File

@@ -135,7 +135,7 @@ namespace ams::kern {
namespace {
constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable);
constexpr size_t CarveoutAlignment = 0x20000;
constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment;

View File

@@ -104,7 +104,7 @@ namespace ams::kern {
Impl *chosen_manager = nullptr;
KVirtualAddress allocated_block = Null<KVirtualAddress>;
for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; chosen_manager = this->GetNextManager(chosen_manager, dir)) {
allocated_block = chosen_manager->AllocateBlock(heap_index, true);
allocated_block = chosen_manager->AllocateBlock(heap_index);
if (allocated_block != Null<KVirtualAddress>) {
break;
}
@@ -129,7 +129,19 @@ namespace ams::kern {
return allocated_block;
}
Result KMemoryManager::AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool optimize, bool random) {
Result KMemoryManager::Allocate(KPageGroup *out, size_t num_pages, u32 option) {
MESOSPHERE_ASSERT(out != nullptr);
MESOSPHERE_ASSERT(out->GetNumPages() == 0);
/* Early return if we're allocating no pages. */
if (num_pages == 0) {
return ResultSuccess();
}
/* Lock the pool that we're allocating from. */
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(this->pool_locks[pool]);
/* Choose a heap based on our page size request. */
const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
R_UNLESS(0 <= heap_index, svc::ResultOutOfMemory());
@@ -150,7 +162,7 @@ namespace ams::kern {
for (Impl *cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr; cur_manager = this->GetNextManager(cur_manager, dir)) {
while (num_pages >= pages_per_alloc) {
/* Allocate a block. */
KVirtualAddress allocated_block = cur_manager->AllocateBlock(index, random);
KVirtualAddress allocated_block = cur_manager->AllocateBlock(index);
if (allocated_block == Null<KVirtualAddress>) {
break;
}
@@ -163,7 +175,7 @@ namespace ams::kern {
}
/* Maintain the optimized memory bitmap, if we should. */
if (optimize) {
if (this->has_optimized_process[pool]) {
cur_manager->TrackAllocationForOptimizedProcess(allocated_block, pages_per_alloc);
}
@@ -181,21 +193,6 @@ namespace ams::kern {
return ResultSuccess();
}
Result KMemoryManager::Allocate(KPageGroup *out, size_t num_pages, u32 option) {
MESOSPHERE_ASSERT(out != nullptr);
MESOSPHERE_ASSERT(out->GetNumPages() == 0);
/* Early return if we're allocating no pages. */
R_SUCCEED_IF(num_pages == 0);
/* Lock the pool that we're allocating from. */
const auto [pool, dir] = DecodeOption(option);
KScopedLightLock lk(this->pool_locks[pool]);
/* Allocate the page group. */
return this->AllocatePageGroupImpl(out, num_pages, pool, dir, this->has_optimized_process[pool], true);
}
size_t KMemoryManager::Impl::Initialize(const KMemoryRegion *region, Pool p, KVirtualAddress metadata, KVirtualAddress metadata_end) {
/* Calculate metadata sizes. */
const size_t ref_count_size = (region->GetSize() / PageSize) * sizeof(u16);

View File

@@ -51,11 +51,11 @@ namespace ams::kern {
return num_free;
}
KVirtualAddress KPageHeap::AllocateBlock(s32 index, bool random) {
KVirtualAddress KPageHeap::AllocateBlock(s32 index) {
const size_t needed_size = this->blocks[index].GetSize();
for (s32 i = index; i < static_cast<s32>(this->num_blocks); i++) {
if (const KVirtualAddress addr = this->blocks[i].PopBlock(random); addr != Null<KVirtualAddress>) {
if (const KVirtualAddress addr = this->blocks[i].PopBlock(); addr != Null<KVirtualAddress>) {
if (const size_t allocated_size = this->blocks[i].GetSize(); allocated_size > needed_size) {
this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}

View File

@@ -20,36 +20,35 @@ namespace ams::kern {
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
/* Initialize our members. */
this->address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32);
this->address_space_start = KProcessAddress(GetInteger(start));
this->address_space_end = KProcessAddress(GetInteger(end));
this->is_kernel = true;
this->enable_aslr = true;
this->address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32);
this->address_space_start = KProcessAddress(GetInteger(start));
this->address_space_end = KProcessAddress(GetInteger(end));
this->is_kernel = true;
this->enable_aslr = true;
this->heap_region_start = 0;
this->heap_region_end = 0;
this->current_heap_end = 0;
this->alias_region_start = 0;
this->alias_region_end = 0;
this->stack_region_start = 0;
this->stack_region_end = 0;
this->kernel_map_region_start = 0;
this->kernel_map_region_end = 0;
this->alias_code_region_start = 0;
this->alias_code_region_end = 0;
this->code_region_start = 0;
this->code_region_end = 0;
this->max_heap_size = 0;
this->max_physical_memory_size = 0;
this->mapped_unsafe_physical_memory = 0;
this->heap_region_start = 0;
this->heap_region_end = 0;
this->current_heap_end = 0;
this->alias_region_start = 0;
this->alias_region_end = 0;
this->stack_region_start = 0;
this->stack_region_end = 0;
this->kernel_map_region_start = 0;
this->kernel_map_region_end = 0;
this->alias_code_region_start = 0;
this->alias_code_region_end = 0;
this->code_region_start = 0;
this->code_region_end = 0;
this->max_heap_size = 0;
this->max_physical_memory_size = 0;
this->memory_block_slab_manager = std::addressof(Kernel::GetSystemMemoryBlockManager());
this->block_info_manager = std::addressof(Kernel::GetBlockInfoManager());
this->memory_block_slab_manager = std::addressof(Kernel::GetSystemMemoryBlockManager());
this->block_info_manager = std::addressof(Kernel::GetBlockInfoManager());
this->allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
this->heap_fill_value = MemoryFillValue_Zero;
this->ipc_fill_value = MemoryFillValue_Zero;
this->stack_fill_value = MemoryFillValue_Zero;
this->allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
this->heap_fill_value = MemoryFillValue_Zero;
this->ipc_fill_value = MemoryFillValue_Zero;
this->stack_fill_value = MemoryFillValue_Zero;
this->cached_physical_linear_region = nullptr;
this->cached_physical_heap_region = nullptr;
@@ -223,10 +222,9 @@ namespace ams::kern {
}
/* Set heap and fill members. */
this->current_heap_end = this->heap_region_start;
this->max_heap_size = 0;
this->max_physical_memory_size = 0;
this->mapped_unsafe_physical_memory = 0;
this->current_heap_end = this->heap_region_start;
this->max_heap_size = 0;
this->max_physical_memory_size = 0;
const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled();
this->heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero;

View File

@@ -35,8 +35,7 @@ namespace ams::kern {
/* Create and clear the process local region. */
R_TRY(this->CreateThreadLocalRegion(std::addressof(this->plr_address)));
this->plr_heap_address = this->GetThreadLocalRegionPointer(this->plr_address);
std::memset(this->plr_heap_address, 0, ams::svc::ThreadLocalRegionSize);
std::memset(this->GetThreadLocalRegionPointer(this->plr_address), 0, ams::svc::ThreadLocalRegionSize);
/* Copy in the name from parameters. */
static_assert(sizeof(params.name) < sizeof(this->name));

View File

@@ -19,8 +19,6 @@ namespace ams::kern {
namespace {
KDynamicPageManager g_resource_manager_page_manager;
template<typename T>
ALWAYS_INLINE void PrintMemoryRegion(const char *prefix, const T &extents) {
static_assert(std::is_same<decltype(extents.GetAddress()), uintptr_t>::value);
@@ -90,29 +88,24 @@ namespace ams::kern {
void Kernel::InitializeResourceManagers(KVirtualAddress address, size_t size) {
/* Ensure that the buffer is suitable for our use. */
//const size_t app_size = ApplicationMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
//const size_t sys_size = SystemMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
//const size_t info_size = BlockInfoSlabHeapSize * sizeof(KBlockInfo);
//const size_t fixed_size = util::AlignUp(app_size + sys_size + info_size, PageSize);
const size_t app_size = ApplicationMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
const size_t sys_size = SystemMemoryBlockSlabHeapSize * sizeof(KMemoryBlock);
const size_t info_size = BlockInfoSlabHeapSize * sizeof(KBlockInfo);
const size_t fixed_size = util::AlignUp(app_size + sys_size + info_size, PageSize);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(fixed_size < size);
/* Ensure that we have space for our reference counts. */
const size_t rc_size = util::AlignUp(KPageTableManager::CalculateReferenceCountSize(size), PageSize);
MESOSPHERE_ABORT_UNLESS(rc_size < size);
size -= rc_size;
size_t pt_size = size - fixed_size;
const size_t rc_size = util::AlignUp(KPageTableManager::CalculateReferenceCountSize(pt_size), PageSize);
MESOSPHERE_ABORT_UNLESS(rc_size < pt_size);
pt_size -= rc_size;
/* Initialize the resource managers' shared page manager. */
g_resource_manager_page_manager.Initialize(address, size);
/* Initialize the fixed-size slabheaps. */
s_app_memory_block_manager.Initialize(std::addressof(g_resource_manager_page_manager), ApplicationMemoryBlockSlabHeapSize);
s_sys_memory_block_manager.Initialize(std::addressof(g_resource_manager_page_manager), SystemMemoryBlockSlabHeapSize);
s_block_info_manager.Initialize(std::addressof(g_resource_manager_page_manager), BlockInfoSlabHeapSize);
/* Reserve all remaining pages for the page table manager. */
const size_t num_pt_pages = g_resource_manager_page_manager.GetCount() - g_resource_manager_page_manager.GetUsed();
s_page_table_manager.Initialize(std::addressof(g_resource_manager_page_manager), num_pt_pages, GetPointer<KPageTableManager::RefCount>(address + size));
/* Initialize the slabheaps. */
s_app_memory_block_manager.Initialize(address + pt_size, app_size);
s_sys_memory_block_manager.Initialize(address + pt_size + app_size, sys_size);
s_block_info_manager.Initialize(address + pt_size + app_size + sys_size, info_size);
s_page_table_manager.Initialize(address, pt_size, GetPointer<KPageTableManager::RefCount>(address + pt_size + fixed_size));
}
void Kernel::PrintLayout() {

View File

@@ -14,7 +14,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libstratosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project under the Zero-Clause BSD license.
Credits

View File

@@ -19,9 +19,6 @@
/* libvapours (pulls in util, svc, results). */
#include <vapours.hpp>
/* Libstratosphere definitions. */
#include <stratosphere/ams/impl/ams_system_thread_definitions.hpp>
/* Libstratosphere-only utility. */
#include <stratosphere/util.hpp>
@@ -41,7 +38,6 @@
/* At this point, just include the rest alphabetically. */
/* TODO: Figure out optimal order. */
#include <stratosphere/boot2.hpp>
#include <stratosphere/capsrv.hpp>
#include <stratosphere/cfg.hpp>
#include <stratosphere/dmnt.hpp>
#include <stratosphere/erpt.hpp>

View File

@@ -22,8 +22,4 @@ namespace ams {
/* Will be called by libstratosphere on crash. */
void CrashHandler(ThreadExceptionDump *ctx);
/* API for boot sysmodule. */
void InitializeForBoot();
void SetInitialRebootPayload(const void *src, size_t src_size);
}

View File

@@ -27,11 +27,6 @@ namespace ams::exosphere {
bool IsRcmBugPatched();
bool ShouldBlankProdInfo();
bool ShouldAllowWritesToProdInfo();
u64 GetDeviceId();
void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size);
void CopyFromIram(void *dram_dst, uintptr_t iram_src, size_t size);

View File

@@ -1,117 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::impl {
struct SystemThreadDefinition {
s32 priority;
const char *name;
};
#define AMS_DEFINE_SYSTEM_THREAD(__AMS_THREAD_PRIORITY__, __AMS_MODULE__, __AMS_THREAD_NAME__) \
constexpr inline const ::ams::impl::SystemThreadDefinition SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__ = { __AMS_THREAD_PRIORITY__, "ams." # __AMS_MODULE__ "." #__AMS_THREAD_NAME__ }
/* sm. */
AMS_DEFINE_SYSTEM_THREAD(-1, sm, Main);
/* spl. */
AMS_DEFINE_SYSTEM_THREAD(-1, spl, Main);
/* Loader. */
AMS_DEFINE_SYSTEM_THREAD(21, ldr, Main);
/* Process Manager. */
AMS_DEFINE_SYSTEM_THREAD(21, pm, Main);
AMS_DEFINE_SYSTEM_THREAD(21, pm, ProcessTrack);
/* NCM. */
AMS_DEFINE_SYSTEM_THREAD(21, ncm, MainWaitThreads);
AMS_DEFINE_SYSTEM_THREAD(21, ncm, ContentManagerServerIpcSession);
AMS_DEFINE_SYSTEM_THREAD(21, ncm, LocationResolverServerIpcSession);
/* FS. */
AMS_DEFINE_SYSTEM_THREAD(16, fs, WorkerThreadPool);
AMS_DEFINE_SYSTEM_THREAD(17, fs, Main);
AMS_DEFINE_SYSTEM_THREAD(17, fs, WorkerRealTimeAccess);
AMS_DEFINE_SYSTEM_THREAD(18, fs, WorkerNormalPriorityAccess);
AMS_DEFINE_SYSTEM_THREAD(19, fs, WorkerLowPriorityAccess);
AMS_DEFINE_SYSTEM_THREAD(30, fs, WorkerBackgroundAccess);
AMS_DEFINE_SYSTEM_THREAD(30, fs, PatrolReader);
/* Boot. */
AMS_DEFINE_SYSTEM_THREAD(-1, boot, Main);
/* Mitm. */
AMS_DEFINE_SYSTEM_THREAD(-7, mitm, InitializeThread);
AMS_DEFINE_SYSTEM_THREAD(-1, mitm_sf, QueryServerProcessThread);
AMS_DEFINE_SYSTEM_THREAD(16, mitm_fs, RomFileSystemInitializeThread);
AMS_DEFINE_SYSTEM_THREAD(21, mitm, DebugThrowThread);
/* boot2. */
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
/* dmnt. */
AMS_DEFINE_SYSTEM_THREAD(-3, dmnt, MultiCoreEventManager);
AMS_DEFINE_SYSTEM_THREAD(-1, dmnt, CheatDebugEvents);
AMS_DEFINE_SYSTEM_THREAD(-1, dmnt, MultiCoreBP);
AMS_DEFINE_SYSTEM_THREAD(11, dmnt, Main);
AMS_DEFINE_SYSTEM_THREAD(11, dmnt, Ipc);
AMS_DEFINE_SYSTEM_THREAD(11, dmnt, CheatDetect);
AMS_DEFINE_SYSTEM_THREAD(20, dmnt, CheatVirtualMachine);
/* fatal */
AMS_DEFINE_SYSTEM_THREAD(-13, fatal, Main);
AMS_DEFINE_SYSTEM_THREAD(-13, fatalsrv, FatalTaskThread);
AMS_DEFINE_SYSTEM_THREAD( 9, fatalsrv, IpcDispatcher);
/* creport. */
AMS_DEFINE_SYSTEM_THREAD(16, creport, Main);
/* ro. */
AMS_DEFINE_SYSTEM_THREAD(16, ro, Main);
/* bpc. */
AMS_DEFINE_SYSTEM_THREAD(4, bpc, IpcServer);
/* hid. */
AMS_DEFINE_SYSTEM_THREAD(-10, hid, IpcServer);
/* ns.*/
AMS_DEFINE_SYSTEM_THREAD(21, ns, ApplicationManagerIpcSession);
/* settings. */
AMS_DEFINE_SYSTEM_THREAD(21, settings, Main);
AMS_DEFINE_SYSTEM_THREAD(21, settings, IpcServer);
/* erpt. */
AMS_DEFINE_SYSTEM_THREAD(21, erpt, Main);
AMS_DEFINE_SYSTEM_THREAD(21, erpt, IpcServer);
/* jpegdec. */
AMS_DEFINE_SYSTEM_THREAD(21, jpegdec, Main);
/* pgl. */
AMS_DEFINE_SYSTEM_THREAD(21, pgl, Main);
AMS_DEFINE_SYSTEM_THREAD(21, pgl, ProcessControlTask);
#undef AMS_DEFINE_SYSTEM_THREAD
}
#define AMS_GET_SYSTEM_THREAD_PRIORITY(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.priority
#define AMS_GET_SYSTEM_THREAD_NAME(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.name

View File

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

View File

@@ -1,30 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/vi/vi_layer_stack.hpp>
namespace ams::capsrv {
constexpr inline s32 DefaultCaptureTimeoutMilliSeconds = 100;
Result InitializeScreenShotControl();
void FinalizeScreenShotControl();
Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout);
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::capsrv {
enum ScreenShotDecoderFlag : u64 {
ScreenShotDecoderFlag_None = (0 << 0),
ScreenShotDecoderFlag_EnableFancyUpsampling = (1 << 0),
ScreenShotDecoderFlag_EnableBlockSmoothing = (1 << 1),
};
using ScreenShotJpegDecoderFlagType = typename std::underlying_type<ScreenShotDecoderFlag>::type;
struct ScreenShotDecodeOption {
ScreenShotJpegDecoderFlagType flags;
u8 reserved[0x20 - sizeof(ScreenShotJpegDecoderFlagType)];
static constexpr ScreenShotDecodeOption GetDefaultOption() {
return ScreenShotDecodeOption{};
}
constexpr bool HasJpegDecoderFlag(ScreenShotJpegDecoderFlagType flag) const {
return (this->flags & flag) != 0;
}
};
static_assert(sizeof(ScreenShotDecodeOption) == 0x20);
static_assert(sizeof(ScreenShotDecodeOption) == sizeof(::CapsScreenShotDecodeOption));
static_assert(std::is_pod<ScreenShotDecodeOption>::value);
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::capsrv::server {
constexpr inline int ScreenShotWidth = 1280;
constexpr inline int ScreenShotHeight = 720;
constexpr inline int MovieWidth = 1280;
constexpr inline int MovieHeight = 720;
constexpr inline size_t SoftwareJpegDecoderWorkMemorySize = 16_KB;
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::capsrv::server {
Result InitializeForDecoderServer();
void FinalizeForDecoderServer();
void DecoderControlServerThreadFunction(void *);
}

View File

@@ -26,33 +26,14 @@ namespace ams::fssystem {
Relative,
};
private:
os::ThreadType *thread;
s32 priority;
/* TODO */
public:
ALWAYS_INLINE explicit ScopedThreadPriorityChanger(s32 prio, Mode mode) : thread(os::GetCurrentThread()), priority(0) {
const auto result_priority = std::min((mode == Mode::Relative) ? os::GetThreadPriority(this->thread) + priority : priority, os::LowestSystemThreadPriority);
this->priority = os::ChangeThreadPriority(thread, result_priority);
ALWAYS_INLINE explicit ScopedThreadPriorityChanger(s32 priority, Mode mode) {
/* TODO */
}
ALWAYS_INLINE ~ScopedThreadPriorityChanger() {
os::ChangeThreadPriority(this->thread, this->priority);
/* TODO */
}
};
class ScopedThreadPriorityChangerByAccessPriority {
public:
enum class AccessMode {
Read,
Write,
};
private:
static s32 GetThreadPriorityByAccessPriority(AccessMode mode);
private:
ScopedThreadPriorityChanger scoped_changer;
public:
ALWAYS_INLINE explicit ScopedThreadPriorityChangerByAccessPriority(AccessMode mode) : scoped_changer(GetThreadPriorityByAccessPriority(mode), ScopedThreadPriorityChanger::Mode::Absolute) {
/* ... */
}
};
}

View File

@@ -16,6 +16,5 @@
#pragma once
#include <stratosphere/hos/hos_types.hpp>
#include <stratosphere/hos/hos_version_api.hpp>
#include <stratosphere/hos/hos_stratosphere_api.hpp>
#include "hos/hos_types.hpp"
#include "hos/hos_version_api.hpp"

View File

@@ -1,24 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/hos/hos_types.hpp>
namespace ams::hos {
void InitializeForStratosphere();
}

View File

@@ -15,10 +15,11 @@
*/
#pragma once
#include <stratosphere/hos/hos_types.hpp>
#include "hos_types.hpp"
namespace ams::hos {
::ams::hos::Version GetVersion();
void SetVersionForLibnx();
}

View File

@@ -19,18 +19,12 @@
namespace ams::os::impl {
#if defined(ATMOSPHERE_OS_HORIZON)
class ReadWriteLockHorizonImpl;
#endif
class ReadWriteLockImpl;
class InternalConditionVariableImpl;
class InternalCriticalSectionImpl {
private:
#if defined(ATMOSPHERE_OS_HORIZON)
friend class ReadWriteLockHorizonImpl;
#endif
friend class ReadWriteLockImpl;
friend class InternalConditionVariableImpl;
private:
u32 thread_handle;

View File

@@ -15,9 +15,7 @@
*/
#pragma once
#include <stratosphere/os/os_rw_lock_common.hpp>
#include <stratosphere/os/os_rw_lock_types.hpp>
#include <stratosphere/os/os_rw_lock_api.hpp>
#include "os_common_types.hpp"
namespace ams::os {
@@ -25,83 +23,67 @@ namespace ams::os {
NON_COPYABLE(ReadWriteLock);
NON_MOVEABLE(ReadWriteLock);
private:
ReadWriteLockType rw_lock;
::RwLock r;
public:
constexpr explicit ReadWriteLock() : rw_lock{{}, 0, ::ams::os::ReadWriteLockType::State_Initialized, nullptr, 0, {}, {}} { /* ... */ }
~ReadWriteLock() { os::FinalizeReadWriteLock(std::addressof(this->rw_lock)); }
void AcquireReadLock() {
return os::AcquireReadLock(std::addressof(this->rw_lock));
}
bool TryAcquireReadLock() {
return os::TryAcquireReadLock(std::addressof(this->rw_lock));
}
void ReleaseReadLock() {
return os::ReleaseReadLock(std::addressof(this->rw_lock));
}
void AcquireWriteLock() {
return os::AcquireWriteLock(std::addressof(this->rw_lock));
}
bool TryAcquireWriteLock() {
return os::TryAcquireWriteLock(std::addressof(this->rw_lock));
}
void ReleaseWriteLock() {
return os::ReleaseWriteLock(std::addressof(this->rw_lock));
}
bool IsReadLockHeld() const {
return os::IsReadLockHeld(std::addressof(this->rw_lock));
ReadWriteLock() {
rwlockInit(&this->r);
}
bool IsWriteLockHeldByCurrentThread() const {
return os::IsWriteLockHeldByCurrentThread(std::addressof(this->rw_lock));
return rwlockIsWriteLockHeldByCurrentThread(const_cast<::RwLock *>(&this->r));
}
bool IsLockOwner() const {
return os::IsReadWriteLockOwnerThread(std::addressof(this->rw_lock));
return rwlockIsOwnedByCurrentThread(const_cast<::RwLock *>(&this->r));
}
void AcquireReadLock() {
rwlockReadLock(&this->r);
}
void ReleaseReadLock() {
rwlockReadUnlock(&this->r);
}
bool TryAcquireReadLock() {
return rwlockTryReadLock(&this->r);
}
void AcquireWriteLock() {
rwlockWriteLock(&this->r);
}
void ReleaseWriteLock() {
rwlockWriteUnlock(&this->r);
}
bool TryAcquireWriteLock() {
return rwlockTryWriteLock(&this->r);
}
void lock_shared() {
return this->AcquireReadLock();
this->AcquireReadLock();
}
void unlock_shared() {
this->ReleaseReadLock();
}
bool try_lock_shared() {
return this->TryAcquireReadLock();
}
void unlock_shared() {
return this->ReleaseReadLock();
void lock() {
this->AcquireWriteLock();
}
void lock() {
return this->AcquireWriteLock();
void unlock() {
this->ReleaseWriteLock();
}
bool try_lock() {
return this->TryAcquireWriteLock();
}
void unlock() {
return this->ReleaseWriteLock();
}
operator ReadWriteLockType &() {
return this->rw_lock;
}
operator const ReadWriteLockType &() const {
return this->rw_lock;
}
ReadWriteLockType *GetBase() {
return std::addressof(this->rw_lock);
}
};
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_rw_lock_common.hpp>
namespace ams::os {
struct ReadWriteLockType;
void InitalizeReadWriteLock(ReadWriteLockType *rw_lock);
void FinalizeReadWriteLock(ReadWriteLockType *rw_lock);
void AcquireReadLock(ReadWriteLockType *rw_lock);
bool TryAcquireReadLock(ReadWriteLockType *rw_lock);
void ReleaseReadLock(ReadWriteLockType *rw_lock);
void AcquireWriteLock(ReadWriteLockType *rw_lock);
bool TryAcquireWriteLock(ReadWriteLockType *rw_lock);
void ReleaseWriteLock(ReadWriteLockType *rw_lock);
bool IsReadLockHeld(const ReadWriteLockType *rw_lock);
bool IsWriteLockHeldByCurrentThread(const ReadWriteLockType *rw_lock);
bool IsReadWriteLockOwnerThread(const ReadWriteLockType *rw_lock);
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
constexpr inline s32 ReadWriteLockCountMax = (1 << (BITSIZEOF(u16) - 1)) - 1;
constexpr inline s32 ReadWriteLockWaiterCountMax = (1 << BITSIZEOF(u8)) - 1;
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
struct ThreadType;
struct ReadWriteLockType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
struct LockCount {
union {
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
impl::InternalCriticalSectionStorage cs_storage;
};
util::BitPack32 counter;
};
static_assert(std::is_pod<LockCount>::value);
static_assert(std::is_trivial<LockCount>::value);
union {
struct {
LockCount c;
u32 write_lock_count;
} aligned;
struct {
u32 write_lock_count;
LockCount c;
} not_aligned;
} lock_count;
u32 reserved_1;
u8 state;
ThreadType *owner_thread;
u32 reserved_2;
union {
s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)];
impl::InternalConditionVariableStorage _storage;
} cv_read_lock;
union {
s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)];
impl::InternalConditionVariableStorage _storage;
} cv_write_lock;
};
static_assert(std::is_trivial<ReadWriteLockType>::value);
}

View File

@@ -22,7 +22,6 @@ namespace ams::os {
/* Tick API. */
Tick GetSystemTick();
Tick GetSystemTickOrdered();
s64 GetSystemTickFrequency();
TimeSpan ConvertToTimeSpan(Tick tick);
Tick ConvertToTick(TimeSpan ts);

View File

@@ -16,13 +16,11 @@
#pragma once
#include <stratosphere/settings/settings_types.hpp>
#include <stratosphere/settings/settings_fwdbg_types.hpp>
#include <stratosphere/settings/settings_fwdbg_api.hpp>
#include <stratosphere/settings/factory/settings_serial_number.hpp>
#include <stratosphere/settings/factory/settings_device_certificate.hpp>
#include <stratosphere/settings/system/settings_error_report.hpp>
#include <stratosphere/settings/system/settings_firmware_version.hpp>
#include <stratosphere/settings/system/settings_product_model.hpp>
#include <stratosphere/settings/system/settings_region.hpp>
#include <stratosphere/settings/system/settings_serial_number.hpp>
#include "settings/settings_types.hpp"
#include "settings/settings_fwdbg_types.hpp"
#include "settings/settings_fwdbg_api.hpp"
#include "settings/system/settings_error_report.hpp"
#include "settings/system/settings_firmware_version.hpp"
#include "settings/system/settings_product_model.hpp"
#include "settings/system/settings_region.hpp"
#include "settings/system/settings_serial_number.hpp"

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::settings::factory {
struct EccP256DeviceCertificate {
u8 data[0x180];
};
static_assert(sizeof(EccP256DeviceCertificate) == 0x180);
static_assert(std::is_pod<EccP256DeviceCertificate>::value);
struct EccB233DeviceCertificate {
u8 data[0x180];
};
static_assert(sizeof(EccB233DeviceCertificate) == 0x180);
static_assert(std::is_pod<EccB233DeviceCertificate>::value);
struct Rsa2048DeviceCertificate {
u8 data[0x240];
};
static_assert(sizeof(Rsa2048DeviceCertificate) == 0x240);
static_assert(std::is_pod<Rsa2048DeviceCertificate>::value);
}

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
#include <vapours.hpp>
namespace ams::settings::factory {
struct SerialNumber {
char str[0x18];
};
static_assert(sizeof(SerialNumber) == 0x18);
static_assert(std::is_pod<SerialNumber>::value);
}

View File

@@ -22,9 +22,6 @@
#include <stratosphere/sf/sf_service_object.hpp>
#include <stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp>
#include <stratosphere/sf/cmif/sf_cmif_inline_context.hpp>
#include <stratosphere/sf/sf_fs_inline_context.hpp>
#include <stratosphere/sf/sf_out.hpp>
#include <stratosphere/sf/sf_buffers.hpp>
#include <stratosphere/sf/impl/sf_impl_command_serialization.hpp>

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/sf/sf_common.hpp>
namespace ams::sf::cmif {
using InlineContext = u32;
InlineContext GetInlineContext();
InlineContext SetInlineContext(InlineContext ctx);
class ScopedInlineContextChanger {
private:
InlineContext prev_ctx;
public:
ALWAYS_INLINE explicit ScopedInlineContextChanger(InlineContext new_ctx) : prev_ctx(SetInlineContext(new_ctx)) { /* ... */ }
~ScopedInlineContextChanger() { SetInlineContext(this->prev_ctx); }
};
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/sf/sf_common.hpp>
namespace ams::sf {
u8 GetFsInlineContext();
u8 SetFsInlineContext(u8 ctx);
}

View File

@@ -27,7 +27,4 @@ namespace ams::spl {
bool IsMariko();
bool IsRecoveryBoot();
Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, u32 generation, u32 option);
Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size);
}

View File

@@ -183,36 +183,6 @@ namespace ams::spl {
static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");
#pragma pack(pop)
enum class ConfigItem : u32 {
/* Standard config items. */
DisableProgramVerification = 1,
DramId = 2,
SecurityEngineIrqNumber = 3,
Version = 4,
HardwareType = 5,
IsRetail = 6,
IsRecoveryBoot = 7,
DeviceId = 8,
BootReason = 9,
MemoryMode = 10,
IsDebugMode = 11,
KernelConfiguration = 12,
IsChargerHiZModeEnabled = 13,
IsQuest = 14,
RegulatorType = 15,
DeviceUniqueKeyGeneration = 16,
Package2Hash = 17,
/* Extension config items for exosphere. */
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
};
}
/* Extensions to libnx spl config item enum. */
@@ -221,5 +191,3 @@ constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsReboot = static_ca
constexpr inline SplConfigItem SplConfigItem_ExosphereNeedsShutdown = static_cast<SplConfigItem>(65002);
constexpr inline SplConfigItem SplConfigItem_ExosphereGitCommitHash = static_cast<SplConfigItem>(65003);
constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_cast<SplConfigItem>(65004);
constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast<SplConfigItem>(65005);
constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast<SplConfigItem>(65006);

View File

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

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::vi {
enum LayerStack {
LayerStack_Default = 0,
LayerStack_Lcd = 1,
LayerStack_Screenshot = 2,
LayerStack_Recording = 3,
LayerStack_LastFrame = 4,
LayerStack_Arbitrary = 5,
LayerStack_ApplicationForDebug = 6,
LayerStack_Null = 10,
};
}

View File

@@ -24,11 +24,6 @@ NX_GENERATE_SERVICE_GUARD(amsBpc);
Result _amsBpcInitialize(void) {
Handle h;
Result rc = svcConnectToNamedPort(&h, "bpc:ams"); /* TODO: ams:bpc */
while (R_VALUE(rc) == KERNELRESULT(NotFound)) {
svcSleepThread(50000000ul);
rc = svcConnectToNamedPort(&h, "bpc:ams");
}
if (R_SUCCEEDED(rc)) serviceCreate(&g_amsBpcSrv, h);
return rc;
}
@@ -49,11 +44,3 @@ Result amsBpcRebootToFatalError(void *ctx) {
.buffers = { { ctx, 0x450 } },
);
}
Result amsBpcSetInitialPayload(const void *src, size_t src_size) {
return serviceDispatch(&g_amsBpcSrv, 65001,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
.buffers = { { src, src_size } },
);
}

View File

@@ -28,7 +28,6 @@ void amsBpcExit(void);
Service *amsBpcGetServiceSession(void);
Result amsBpcRebootToFatalError(void *ctx);
Result amsBpcSetInitialPayload(const void *src, size_t src_size);
#ifdef __cplusplus
}

View File

@@ -36,14 +36,6 @@ namespace ams {
extern ncm::ProgramId CurrentProgramId;
void InitializeForBoot() {
R_ABORT_UNLESS(amsBpcInitialize());
}
void SetInitialRebootPayload(const void *src, size_t src_size) {
R_ABORT_UNLESS(amsBpcSetInitialPayload(src, src_size));
}
void WEAK_SYMBOL ExceptionHandler(FatalErrorContext *ctx) {
R_ABORT_UNLESS(amsBpcInitialize());
R_ABORT_UNLESS(amsBpcRebootToFatalError(ctx));

View File

@@ -26,7 +26,7 @@ namespace ams::exosphere {
R_ABORT_UNLESS(ResultNotPresent());
}
return ApiInfo{ util::BitPack64{exosphere_cfg} };
return ApiInfo{ util::BitPack64(exosphere_cfg) };
}
void ForceRebootToRcm() {
@@ -51,32 +51,19 @@ namespace ams::exosphere {
namespace {
inline u64 GetU64ConfigItem(spl::ConfigItem cfg) {
u64 tmp;
R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::GetConfig(std::addressof(tmp), 1, static_cast<::SplConfigItem>(cfg))));
return tmp;
}
inline bool GetBooleanConfigItem(spl::ConfigItem cfg) {
return GetU64ConfigItem(cfg) != 0;
inline Result GetRcmBugPatched(bool *out) {
u64 tmp = 0;
R_TRY(spl::smc::ConvertResult(spl::smc::GetConfig(&tmp, 1, SplConfigItem_ExosphereHasRcmBugPatch)));
*out = (tmp != 0);
return ResultSuccess();
}
}
bool IsRcmBugPatched() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereHasRcmBugPatch);
}
bool ShouldBlankProdInfo() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereBlankProdInfo);
}
bool ShouldAllowWritesToProdInfo() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereAllowCalWrites);
}
u64 GetDeviceId() {
return GetU64ConfigItem(spl::ConfigItem::DeviceId);
bool rcm_bug_patched;
R_ABORT_UNLESS(GetRcmBugPatched(&rcm_bug_patched));
return rcm_bug_patched;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::capsrv {
Result InitializeScreenShotControl() {
return ::capsscInitialize();
}
void FinalizeScreenShotControl() {
return ::capsscExit();
}
Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout) {
return ::capsscCaptureJpegScreenShot(out_size, dst, dst_size, static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds());
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv/decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
namespace {
bool g_initialized = false;
}
Result InitializeForDecoderServer() {
/* Ensure we initialize only once. */
AMS_ABORT_UNLESS(!g_initialized);
/* Clear work memory. */
std::memset(std::addressof(g_work_memory), 0, sizeof(g_work_memory));
/* Initialize the decoder server. */
R_ABORT_UNLESS(g_decoder_control_server_manager.Initialize());
/* Start the server. */
g_decoder_control_server_manager.StartServer();
/* We're initialized. */
g_initialized = true;
return ResultSuccess();
}
void FinalizeForDecoderServer() {
/* Ensure we don't finalize when uninitialized. */
AMS_ABORT_UNLESS(g_initialized);
/* Finalize the server. */
g_decoder_control_server_manager.Finalize();
/* Mark uninitialized. */
g_initialized = false;
}
void DecoderControlServerThreadFunction(void *) {
/* We need to be initialized. */
AMS_ABORT_UNLESS(g_initialized);
/* Run the server. */
g_decoder_control_server_manager.RunServer();
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
Result DecoderControlServerManager::Initialize() {
/* Create the objects. */
this->service_holder.emplace();
this->server_manager_holder.emplace();
/* Register the service. */
R_ABORT_UNLESS(this->server_manager_holder->RegisterServer<Service>(ServiceName, MaxSessions, sf::ServiceObjectTraits<Service>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(*this->service_holder))));
/* Initialize the idle event, we're idle initially. */
os::InitializeEvent(std::addressof(this->idle_event), true, os::EventClearMode_ManualClear);
return ResultSuccess();
}
void DecoderControlServerManager::Finalize() {
/* Check that the server is idle. */
AMS_ASSERT(os::TryWaitEvent(std::addressof(this->idle_event)));
/* Destroy the server. */
os::FinalizeEvent(std::addressof(this->idle_event));
this->server_manager_holder = std::nullopt;
this->service_holder = std::nullopt;
}
void DecoderControlServerManager::StartServer() {
this->server_manager_holder->ResumeProcessing();
}
void DecoderControlServerManager::StopServer() {
/* Request the server stop, and wait until it does. */
this->server_manager_holder->RequestStopProcessing();
os::WaitEvent(std::addressof(this->idle_event));
}
void DecoderControlServerManager::RunServer() {
/* Ensure that we are allowed to run. */
AMS_ABORT_UNLESS(os::TryWaitEvent(std::addressof(this->idle_event)));
/* Clear the event. */
os::ClearEvent(std::addressof(this->idle_event));
/* Process forever. */
this->server_manager_holder->LoopProcess();
/* Signal that we're idle again. */
os::SignalEvent(std::addressof(this->idle_event));
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "decodersrv_decoder_control_service.hpp"
namespace ams::capsrv::server {
class DecoderControlServerManager {
public:
/* NOTE: Nintendo only allows one session. */
static constexpr inline size_t NumServers = 1;
static constexpr inline size_t MaxSessions = 2;
static constexpr inline sm::ServiceName ServiceName = sm::ServiceName::Encode("caps:dc");
using Service = DecoderControlService;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
using ServerManager = sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions>;
private:
std::optional<Service> service_holder;
std::optional<ServerManager> server_manager_holder;
os::EventType idle_event;
public:
constexpr DecoderControlServerManager() : service_holder(), server_manager_holder(), idle_event{} { /* ... */ }
Result Initialize();
void Finalize();
void StartServer();
void StopServer();
void RunServer();
};
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_decoder_server_object.hpp"
#include "../jpeg/decodersrv_software_jpeg_decoder.hpp"
namespace ams::capsrv::server {
namespace {
Result DecodeJpegImpl(void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
/* Clear the work memory. */
std::memset(work, 0, work_size);
ON_SCOPE_EXIT { std::memset(work, 0, work_size); };
/* Clear the output memory. */
std::memset(dst, 0, dst_size);
auto clear_guard = SCOPE_GUARD { std::memset(dst, 0, dst_size); };
/* Validate parameters. */
R_UNLESS(util::IsAligned(width, 0x10), capsrv::ResultAlbumOutOfRange());
R_UNLESS(util::IsAligned(height, 0x4), capsrv::ResultAlbumOutOfRange());
R_UNLESS(dst != nullptr, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(dst_size >= 4 * width * height, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(src_jpeg != nullptr, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(src_jpeg_size != 0, capsrv::ResultAlbumInvalidFileData());
/* Create the input. */
const jpeg::SoftwareJpegDecoderInput decode_input = {
.jpeg = src_jpeg,
.jpeg_size = src_jpeg_size,
.width = width,
.height = height,
.fancy_upsampling = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableFancyUpsampling),
.block_smoothing = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableBlockSmoothing),
};
/* Create the output. */
s32 out_width = 0, out_height = 0;
jpeg::SoftwareJpegDecoderOutput decode_output = {
.out_width = std::addressof(out_width),
.out_height = std::addressof(out_height),
.dst = dst,
.dst_size = dst_size,
};
/* Decode the jpeg. */
R_TRY(jpeg::SoftwareJpegDecoder::DecodeRgba8(decode_output, decode_input, work, work_size));
/* We succeeded, so we shouldn't clear the output memory. */
clear_guard.Cancel();
return ResultSuccess();
}
}
Result DecoderControlService::DecodeJpeg(const sf::OutNonSecureBuffer &out, const sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option) {
/* Get the work buffer. */
void *work = g_work_memory.jpeg_decoder_memory;
size_t work_size = sizeof(g_work_memory.jpeg_decoder_memory);
/* Call the decoder implementation. */
return DecodeJpegImpl(out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size);
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::capsrv::server {
class DecoderControlService final : public sf::IServiceObject {
protected:
enum class CommandId {
DecodeJpeg = 3001,
};
public:
/* Actual commands. */
virtual Result DecodeJpeg(const sf::OutNonSecureBuffer &out, const sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(DecodeJpeg)
};
};
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_decoder_server_object.hpp"
namespace ams::capsrv::server {
/* Instantiate the decoder server globals in a single TU. */
DecoderWorkMemory g_work_memory;
DecoderControlServerManager g_decoder_control_server_manager;
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "decodersrv_decoder_work_memory.hpp"
#include "decodersrv_decoder_control_server_manager.hpp"
namespace ams::capsrv::server {
extern DecoderWorkMemory g_work_memory;
extern DecoderControlServerManager g_decoder_control_server_manager;
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::capsrv::server {
struct DecoderWorkMemory {
alignas(os::MemoryPageSize) u8 jpeg_decoder_memory[SoftwareJpegDecoderWorkMemorySize];
};
static_assert(sizeof(DecoderWorkMemory) == SoftwareJpegDecoderWorkMemorySize);
static_assert(alignof(DecoderWorkMemory) == os::MemoryPageSize);
static_assert(std::is_pod<DecoderWorkMemory>::value);
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include <csetjmp>
#include "capsrv_server_jpeg_library_types.hpp"
namespace ams::capsrv::server::jpeg {
struct JpegErrorHandler : public JpegLibraryType::jpeg_error_mgr {
public:
std::jmp_buf jmp_buf;
Result result;
public:
static void HandleError(JpegLibraryType::jpeg_common_struct *common) {
/* Retrieve the handler. */
JpegErrorHandler *handler = reinterpret_cast<JpegErrorHandler *>(common->err);
/* Set the result. */
handler->result = GetResult(handler->msg_code, handler->msg_parm.i[0]);
/* Return to the caller. */
longjmp(handler->jmp_buf, -1);
}
static Result GetResult(int msg_code, int msg_param) {
switch (msg_code) {
case JpegLibraryType::J_MESSAGE_CODE::JERR_BUFFER_SIZE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_NO_BACKING_STORE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_OUT_OF_MEMORY:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_CREATE:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_READ:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_SEEK:
case JpegLibraryType::J_MESSAGE_CODE::JERR_TFILE_WRITE:
return capsrv::ResultInternalJpegWorkMemoryShortage();
default:
return capsrv::ResultInternalJpegEncoderError();
}
}
};
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include <jpeglib.h>
#include <jerror.h>
namespace ams::capsrv::server::jpeg {
class JpegLibraryType {
public:
using jpeg_common_struct = ::jpeg_common_struct;
using jpeg_compress_struct = ::jpeg_compress_struct;
using jpeg_decompress_struct = ::jpeg_decompress_struct;
using jpeg_error_mgr = ::jpeg_error_mgr;
using jpeg_destination_mgr = ::jpeg_destination_mgr;
using j_common_ptr = ::j_common_ptr;
using j_compress_ptr = ::j_compress_ptr;
using boolean = ::boolean;
using JOCTET = ::JOCTET;
using JDIMENSION = ::JDIMENSION;
using JSAMPARRAY = ::JSAMPARRAY;
using JSAMPROW = ::JSAMPROW;
using J_COLOR_SPACE = ::J_COLOR_SPACE;
using J_DCT_METHOD = ::J_DCT_METHOD;
using J_MESSAGE_CODE = ::J_MESSAGE_CODE;
};
}

View File

@@ -1,185 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_software_jpeg_decoder.hpp"
#include "capsrv_server_jpeg_library_types.hpp"
#include "capsrv_server_jpeg_error_handler.hpp"
namespace ams::capsrv::server::jpeg {
#define CAPSRV_ABORT_UNLESS(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
AMS_ABORT_UNLESS(__capsrv_assert_res); \
} while (0)
#define CAPSRV_ASSERT(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
R_UNLESS(__capsrv_assert_res, capsrv::ResultAlbumError()); \
} while (0)
namespace {
constexpr s32 ImageSizeHorizonalUnit = 0x10;
constexpr s32 ImageSizeVerticalUnit = 0x4;
constexpr s32 RgbColorComponentCount = 3;
constexpr s32 RgbaColorComponentCount = 4;
Result GetRgbBufferSize(size_t *out_size, size_t *out_stride, s32 width, size_t work_size) {
/* Calculate the space we need and verify we have enough. */
const size_t rgb_width = util::AlignUp(static_cast<size_t>(width), ImageSizeHorizonalUnit);
const size_t rgb_stride = rgb_width * RgbColorComponentCount;
const size_t rgb_size = rgb_stride * ImageSizeVerticalUnit;
R_UNLESS(work_size >= rgb_size, capsrv::ResultInternalJpegWorkMemoryShortage());
/* Return the output to the caller. */
*out_size = rgb_size;
*out_stride = rgb_stride;
return ResultSuccess();
}
}
Result SoftwareJpegDecoder::DecodeRgba8(SoftwareJpegDecoderOutput &output, const SoftwareJpegDecoderInput &input, void *work, size_t work_size) {
CAPSRV_ABORT_UNLESS(util::IsAligned(input.width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(util::IsAligned(input.height, ImageSizeVerticalUnit));
CAPSRV_ABORT_UNLESS(output.dst != nullptr);
CAPSRV_ABORT_UNLESS(output.dst_size >= static_cast<size_t>(RgbaColorComponentCount * input.width * input.height));
CAPSRV_ABORT_UNLESS(output.out_width != nullptr);
CAPSRV_ABORT_UNLESS(output.out_height != nullptr);
/* Determine work buffer extents. */
char *work_start = static_cast<char *>(work);
char *work_end = work_start + work_size;
/* Determine the buffer extents for our linebuffers. */
u8 *rgb_buffer = static_cast<u8 *>(static_cast<void *>(work_start));
size_t rgb_buffer_size;
size_t rgb_buffer_stride;
R_TRY(GetRgbBufferSize(std::addressof(rgb_buffer_size), std::addressof(rgb_buffer_stride), input.width, work_size));
/* The start of the workbuffer is reserved for linebuffer space. */
work_start += rgb_buffer_size;
/* Create our decompression structure. */
JpegLibraryType::jpeg_decompress_struct cinfo = {};
/* Here nintendo creates a work buffer structure containing work_start + work_size. */
/* This seems to be a custom patch for/to libjpeg-turbo. */
/* It would be desirable for us to mimic this, because it gives Nintendo strong */
/* fixed memory usage guarantees. */
/* TODO: Determine if it is feasible for us to recreate this ourselves, */
/* Either by adding support to the devkitPro libjpeg-turbo portlib or otherwise. */
AMS_UNUSED(work_end);
/* Create our error manager. */
JpegErrorHandler jerr = {
.result = ResultSuccess(),
};
jerr.error_exit = JpegErrorHandler::HandleError,
/* Link our error manager to our decompression structure. */
cinfo.err = jpeg_std_error(std::addressof(jerr));
/* Use setjmp, so that on error our handler will longjmp to return an error result. */
if (setjmp(jerr.jmp_buf) == 0) {
/* Create our decompressor. */
jpeg_create_decompress(std::addressof(cinfo));
ON_SCOPE_EXIT { jpeg_destroy_decompress(std::addressof(cinfo)); };
/* Setup our memory reader, ensure the header is correct. */
jpeg_mem_src(std::addressof(cinfo), const_cast<unsigned char *>(static_cast<const unsigned char *>(input.jpeg)), input.jpeg_size);
R_UNLESS(jpeg_read_header(std::addressof(cinfo), true) == JPEG_HEADER_OK, capsrv::ResultAlbumInvalidFileData());
/* Ensure width and height are correct. */
R_UNLESS(cinfo.image_width == input.width, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(cinfo.image_height == input.height, capsrv::ResultAlbumInvalidFileData());
/* Set output parameters. */
cinfo.out_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
cinfo.dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
cinfo.do_fancy_upsampling = input.fancy_upsampling;
cinfo.do_block_smoothing = input.block_smoothing;
/* Start decompression. */
R_UNLESS(jpeg_start_decompress(&cinfo) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Check the parameters. */
CAPSRV_ASSERT(cinfo.output_width == input.width);
CAPSRV_ASSERT(cinfo.output_height == input.height);
CAPSRV_ASSERT(cinfo.out_color_components == RgbColorComponentCount);
CAPSRV_ASSERT(cinfo.output_components == RgbColorComponentCount);
/* Parse the scanlines. */
{
/* Convert our destination to a writable u8 buffer. */
u8 *dst = static_cast<u8 *>(output.dst);
/* Create our linebuffer structure. */
JpegLibraryType::JSAMPROW linebuffers[ImageSizeVerticalUnit] = {};
for (int i = 0; i < ImageSizeVerticalUnit; i++) {
linebuffers[i] = rgb_buffer + rgb_buffer_stride * i;
}
/* While we still have scanlines, parse! */
while (cinfo.output_scanline < input.height) {
/* Decode scanlines. */
int num_scanlines = jpeg_read_scanlines(std::addressof(cinfo), linebuffers, ImageSizeVerticalUnit);
CAPSRV_ASSERT(num_scanlines <= ImageSizeVerticalUnit);
/* Write out line by line. */
for (s32 i = 0; i < num_scanlines; i++) {
const u8 *src = linebuffers[i];
for (s32 j = 0; j < static_cast<s32>(input.width); j++) {
/* Write the output. */
/* First R, */
*(dst++) = *(src++);
/* Then G, */
*(dst++) = *(src++);
/* Then B, */
*(dst++) = *(src++);
/* Then A. */
*(dst++) = 0xFF;
}
}
}
}
/* Finish the decompression. */
R_UNLESS(jpeg_finish_decompress(&cinfo) == TRUE, capsrv::ResultAlbumInvalidFileData());
} else {
/* Some unknown error was caught by our handler. */
return capsrv::ResultAlbumInvalidFileData();
}
/* Write the size we decoded to output. */
*output.out_width = static_cast<s32>(cinfo.output_width);
*output.out_width = static_cast<s32>(cinfo.output_height);
return ResultSuccess();
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::capsrv::server::jpeg {
struct SoftwareJpegDecoderInput {
const void *jpeg;
size_t jpeg_size;
u32 width;
u32 height;
bool fancy_upsampling;
bool block_smoothing;
};
struct SoftwareJpegDecoderOutput {
s32 *out_width;
s32 *out_height;
void *dst;
size_t dst_size;
};
class SoftwareJpegDecoder {
public:
static Result DecodeRgba8(SoftwareJpegDecoderOutput &output, const SoftwareJpegDecoderInput &input, void *work, size_t work_size);
};
}

View File

@@ -58,20 +58,8 @@ Result dmntchtGetCheatProcessMetadata(DmntCheatProcessMetadata *out_metadata) {
return serviceDispatchOut(&g_dmntchtSrv, 65002, *out_metadata);
}
static Result _dmntchtCmdVoid(Service* srv, u32 cmd_id) {
return serviceDispatch(srv, cmd_id);
}
Result dmntchtForceOpenCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65003);
}
Result dmntchtPauseCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65004);
}
Result dmntchtResumeCheatProcess(void) {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65005);
return serviceDispatch(&g_dmntchtSrv, 65003);
}
static Result _dmntchtGetCount(u64 *out_count, u32 cmd_id) {
@@ -154,23 +142,6 @@ Result dmntchtRemoveCheat(u32 cheat_id) {
return _dmntchtCmdInU32NoOut(cheat_id, 65205);
}
Result dmntchtReadStaticRegister(u64 *out, u8 which) {
return serviceDispatchInOut(&g_dmntchtSrv, 65206, which, *out);
}
Result dmntchtWriteStaticRegister(u8 which, u64 value) {
const struct {
u64 which;
u64 value;
} in = { which, value };
return serviceDispatchIn(&g_dmntchtSrv, 65207, in);
}
Result dmntchtResetStaticRegisters() {
return _dmntchtCmdVoid(&g_dmntchtSrv, 65208);
}
Result dmntchtGetFrozenAddressCount(u64 *out_count) {
return _dmntchtGetCount(out_count, 65300);
}

View File

@@ -74,8 +74,6 @@ Result dmntchtGetCheatProcessMappings(MemoryInfo *buffer, u64 max_count, u64 off
Result dmntchtReadCheatProcessMemory(u64 address, void *buffer, size_t size);
Result dmntchtWriteCheatProcessMemory(u64 address, const void *buffer, size_t size);
Result dmntchtQueryCheatProcessMemory(MemoryInfo *mem_info, u64 address);
Result dmntchtPauseCheatProcess(void);
Result dmntchtResumeCheatProcess(void);
Result dmntchtGetCheatCount(u64 *out_count);
Result dmntchtGetCheats(DmntCheatEntry *buffer, u64 max_count, u64 offset, u64 *out_count);
@@ -83,9 +81,6 @@ Result dmntchtGetCheatById(DmntCheatEntry *out_cheat, u32 cheat_id);
Result dmntchtToggleCheat(u32 cheat_id);
Result dmntchtAddCheat(DmntCheatDefinition *cheat, bool enabled, u32 *out_cheat_id);
Result dmntchtRemoveCheat(u32 cheat_id);
Result dmntchtReadStaticRegister(u64 *out, u8 which);
Result dmntchtWriteStaticRegister(u8 which, u64 value);
Result dmntchtResetStaticRegisters();
Result dmntchtGetFrozenAddressCount(u64 *out_count);
Result dmntchtGetFrozenAddresses(DmntFrozenAddressEntry *buffer, u64 max_count, u64 offset, u64 *out_count);

View File

@@ -67,7 +67,7 @@ namespace ams::erpt::srv {
Result Reporter::CollectUniqueReportFields() {
this->occurrence_tick = os::GetSystemTick();
if (hos::GetVersion() >= hos::Version_5_0_0) {
if (hos::GetVersion() >= hos::Version_3_0_0) {
this->steady_clock_internal_offset_seconds = time::GetStandardSteadyClockInternalOffset().GetSeconds();
} else {
this->steady_clock_internal_offset_seconds = 0;

View File

@@ -34,6 +34,8 @@ namespace ams::erpt::srv {
constexpr inline size_t ErrorReportContextSessions = 10;
constexpr inline size_t ErrorReportMaxSessions = ErrorReportReportSessions + ErrorReportContextSessions;
constexpr inline s32 ErrorReportServerThreadPriority = 21;
constexpr inline sm::ServiceName ErrorReportContextServiceName = sm::ServiceName::Encode("erpt:c");
constexpr inline sm::ServiceName ErrorReportReportServiceName = sm::ServiceName::Encode("erpt:r");
@@ -62,8 +64,7 @@ namespace ams::erpt::srv {
this->ResumeProcessing();
R_ABORT_UNLESS(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_server_thread_stack, sizeof(g_server_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(erpt, IpcServer)));
os::SetThreadNamePointer(std::addressof(this->thread), AMS_GET_SYSTEM_THREAD_NAME(erpt, IpcServer));
R_ABORT_UNLESS(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_server_thread_stack, sizeof(g_server_thread_stack), ErrorReportServerThreadPriority));
os::StartThread(std::addressof(this->thread));

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem {
s32 ScopedThreadPriorityChangerByAccessPriority::GetThreadPriorityByAccessPriority(AccessMode mode) {
/* TODO: Actually implement this for real. */
return os::GetThreadPriority(os::GetCurrentThread());
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "hos_version_api_private.hpp"
namespace ams::os {
void InitializeForStratosphereInternal();
}
namespace ams::hos {
void InitializeForStratosphere() {
/* Initialize the global os resource managers. This *must* be done before anything else in stratosphere. */
os::InitializeForStratosphereInternal();
/* Initialize hos::Version API. */
hos::SetVersionForLibnxInternal();
}
}

View File

@@ -13,8 +13,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "hos_version_api_private.hpp"
namespace ams::hos {
@@ -86,7 +86,7 @@ namespace ams::hos {
return g_hos_version;
}
void SetVersionForLibnxInternal() {
void SetVersionForLibnx() {
u32 major = 0, minor = 0, micro = 0;
switch (hos::GetVersion()) {
case hos::Version_1_0_0:

View File

@@ -1,23 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::hos {
void SetVersionForLibnxInternal();
}

View File

@@ -19,6 +19,6 @@
namespace ams::os::impl {
/* TODO: C++20 constinit */
TYPED_STORAGE(OsResourceManager) ResourceManagerHolder::s_resource_manager_storage = {};
OsResourceManager ResourceManagerHolder::s_resource_manager = {};
}

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