Compare commits
48 Commits
qemu_meso
...
allow_stan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51d692dfba | ||
|
|
545ade835d | ||
|
|
be5cd35ef6 | ||
|
|
d30af4763a | ||
|
|
7e110ee8f7 | ||
|
|
ea61ac9ea6 | ||
|
|
95ef9da873 | ||
|
|
d2b024f19f | ||
|
|
9011384dbe | ||
|
|
56669e34b7 | ||
|
|
6df7a87727 | ||
|
|
90473a2307 | ||
|
|
29ffa4b7fd | ||
|
|
a73e0e8f16 | ||
|
|
2161365f4f | ||
|
|
258a83684e | ||
|
|
d2a757c39e | ||
|
|
6cf5205a28 | ||
|
|
b9c90b9234 | ||
|
|
94e18b8c93 | ||
|
|
4e92687cab | ||
|
|
2a0b99d9f9 | ||
|
|
d1f3c4904b | ||
|
|
92321ccbc8 | ||
|
|
db3004e844 | ||
|
|
3e97e4addf | ||
|
|
4b7b33809f | ||
|
|
e81a1ce5a8 | ||
|
|
fb59d0ad43 | ||
|
|
73b74b904f | ||
|
|
ed41b01b69 | ||
|
|
24143d8813 | ||
|
|
cb38b0b929 | ||
|
|
dd04113f5d | ||
|
|
0e81eac9d1 | ||
|
|
a14dc6ed89 | ||
|
|
9cc6be4d57 | ||
|
|
1d5f66be56 | ||
|
|
ebca23305e | ||
|
|
273f4a87ae | ||
|
|
1f8bf41f0b | ||
|
|
ad03be9a38 | ||
|
|
d63be0737b | ||
|
|
018ae08409 | ||
|
|
2a842791eb | ||
|
|
d8a2b47b0a | ||
|
|
7c1347e692 | ||
|
|
0a58e803be |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -91,7 +91,11 @@ dkms.conf
|
||||
**/out
|
||||
**/build
|
||||
**/build_nintendo_nx_arm64
|
||||
**/build_nintendo_nx_arm64_armv8a
|
||||
**/build_nintendo_nx_arm
|
||||
**/build_nintendo_nx_arm_armv8a
|
||||
**/build_nintendo_nx_arm_armv7a
|
||||
**/build_nintendo_nx_arm_armv4t
|
||||
**/build_nintendo_nx_x64
|
||||
**/build_nintendo_nx_x86
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -112,6 +112,7 @@ dist-no-debug: all
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609
|
||||
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623
|
||||
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp
|
||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp
|
||||
@@ -125,6 +126,7 @@ dist-no-debug: all
|
||||
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
|
||||
cp stratosphere/LogManager/LogManager.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
|
||||
cp stratosphere/htc/htc.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240/exefs.nsp
|
||||
cp stratosphere/dmnt.gen2/dmnt.gen2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D609/exefs.nsp
|
||||
cp stratosphere/TioServer/TioServer.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623/exefs.nsp
|
||||
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
|
||||
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
|
||||
|
||||
@@ -1,4 +1,59 @@
|
||||
# Changelog
|
||||
## 1.2.2
|
||||
+ A number of fixes were made to Atmosphère's implementation of the new "sprofile" service added in 13.0.0.
|
||||
+ Nintendo is finally transmitting data over the internet to certain consoles, which has allowed for validating our service implementation.
|
||||
+ Unfortunately, there were several problems, and if your console began trying to use the new services atmosphere would show a fatal error with code 0xCAF6 (sprofile::ResultInvalidState()).
|
||||
+ With actual test data in hand, a test program was written and it was verified that our implementation can successfully import/access profile data now.
|
||||
+ Hopefully there are no more issues, and I sincerely apologize for anyone who got an 0xCAF6 fatal due to this.
|
||||
+ A number of minor improvements were made to `mesosphère`, including:
|
||||
+ KThread::GetContextForSchedulerLoop was implemented in assembly (using static assertions to verify offset-of-context-in-struct is correct).
|
||||
+ This saves an unnecessary function call in the middle of the scheduler hot loop, replacing it with an addition instruction, which should improve microperformance.
|
||||
+ Mesosphere's hardware maintenance instructions were audited via a script and now directly match Nintendo's kernels.
|
||||
+ Notably, this inserts a missing instruction synchronization barrier when validating that slab heaps may be constructed.
|
||||
+ This missing ISB could cause an abort on certain (see: particularly sensitive) hardware on boot if the relevant codepath was speculatively executed (it normally only executes on game launch...)
|
||||
+ The SVC handlers for performing light IPC (normally unused) from 32-bit process were fixed in Mesosphere.
|
||||
+ A bug was fixed that would cause the register x27 to be overwritten with the contents of x26 when returning from a user exception handler.
|
||||
+ A bug was fixed that would cause the kernel to use the userland stack pointer instead of the kernel stack pointer while generating an error report for a kernel abort.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.1
|
||||
+ Support was implemented for 13.1.0.
|
||||
+ `mesosphère` was updated to reflect the kernel behavioral changes made in 13.1.0.
|
||||
+ KScheduler now issues a data memory barrier when unlocking the scheduler lock and when early-returning due to top-thread-is-current during scheduling.
|
||||
+ `erpt` was updated to reflect the latest official behaviors.
|
||||
+ The new service added in 13.0.0 ("sprofile") was revised, and the data formats it expects was changed.
|
||||
+ This still appears to be (possibly(?)) untestable due to data not being transmitted yet, but I have greater confidence things will go smoothly than I did when 1.1.0 released.
|
||||
+ A number of improvements were made to `mesosphère`, including:
|
||||
+ A build target was created to build targeting the qemu `virt` board.
|
||||
+ This facilitates writing unit tests for the kernel (and other atmosphere components) and running them under PC.
|
||||
+ **Please Note**: Official system software will not work at all under this, and the Atmosphère project has zero interest in attempting to run official software of any kind. This is unit testing machinery, and explicitly not more than that.
|
||||
+ This should hopefully allow us to have greater confidence that all of atmosphere's components work the way they're theoretically supposed to in the future.
|
||||
+ **Please Note**: If you are a developer who is familiar with the Horizon operating system (or capable of becoming familiar), I would greatly appreciate help writing tests and improving the testing framework.
|
||||
+ Please contact `SciresM#0524` if you are capable and interested.
|
||||
+ Really, if you are actually a developer who would like to help me get this off the ground, I would deeply appreciate it.
|
||||
+ That said, if you are not a developer but want to be one, this probably isn't the best opportunity; I expect it to be highly technical.
|
||||
+ Consider the ReSwitched discord's #hack-n-all channel for your educational purposes.
|
||||
+ We are (at least for now) using [catch2](https://github.com/catchorg/Catch2) for unit tests.
|
||||
+ Almost all virtual calls in the kernel are now resolved statically.
|
||||
+ This eliminates substantial virtual call overhead, and should lead to improved kernel microperformance in pretty much every function.
|
||||
+ The remaining red black tree find operations which weren't using the optimized "find key" variant are now using the optimized version.
|
||||
+ Custom assembly was written to improve tick-to-timespan conversion.
|
||||
+ This works around gcc emitting suboptimal assembly at -Os (it emits good assembly at -O3, clang is fine at both -O3 and -Os).
|
||||
+ KThread and KSession structures were updated to optimize member layout, saving 0x10 bytes per KThread/KSession object.
|
||||
+ Rather than unnecessarily zero-ing all data in kernel objects only to overwrite members later, we now only initialize the members we need to in kernel object constructors.
|
||||
+ This is what Nintendo was doing already.
|
||||
+ A set of custom optimized atomic primitives were implemented and are used in place of std::atomic<>
|
||||
+ This works around a gcc bug which downgrades specified memory order to seq_cst, and introduces clrex in places where it is appropriate.
|
||||
+ This should strictly improve microperformance of many system calls.
|
||||
+ An compile-time toggleable extension was added to support 40-bit physical addresses in MapRange capabilities (using currently reserved bits).
|
||||
+ A number of minor bugs were fixed, including:
|
||||
+ Initial cache management now better reflects official behavior.
|
||||
+ This fixes an issue that caused certain hardware with cache sensitivity to produce cryptic kernel panics during boot.
|
||||
+ Incorrect logic when checking thread priority capabilities was fixed to reflect official behavior.
|
||||
+ The scheduler was updated to reflect latest official behavior, and a number of minor bugs involving clz/ctz were fixed.
|
||||
+ Accesses to the processes local region were fixed to properly use kernel linear region, not userland pointers.
|
||||
+ The cache SVCs exposed for 32-bit processes now better reflect official core mask request semantics.
|
||||
+ A bug was fixed that could cause a kernel panic if SvcArbitrateLock was called on a thread with exactly one reference in the middle of handling a user-mode exception.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.2.0
|
||||
+ `boot` was updated to reflect the latest official behavior for display/battery management.
|
||||
+ This should fix any issues that might result from running older releases on the OLED model, if you're somehow in a position to do so.
|
||||
|
||||
6
emummc/.gitrepo
vendored
6
emummc/.gitrepo
vendored
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emuMMC
|
||||
branch = develop
|
||||
commit = f66087313546161a000ee196a788f0626caf80fa
|
||||
parent = 38f9a76ba028995ed3274da3a45b0254f09d1f59
|
||||
method = rebase
|
||||
commit = a9d56959460fc794ce2cb6405402c25a3e89c47f
|
||||
parent = ff719641396c635b735873fb2b020c910f768a04
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
16
emummc/README.md
vendored
16
emummc/README.md
vendored
@@ -1,21 +1,21 @@
|
||||
# emuMMC
|
||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||
|
||||
### Supported Horizon Versions
|
||||
**1.0.0 - 13.0.0**
|
||||
**1.0.0 - 13.1.0**
|
||||
|
||||
## Features
|
||||
* Arbitrary SDMMC backend selection
|
||||
* Arbitrary SDMMC backend selection
|
||||
**This allows loading eMMC from SD or even SD from eMMC**
|
||||
* On the fly hooking / patching, fully self-infesting
|
||||
* On the fly hooking / patching, fully self-infesting
|
||||
**Only one payload required for all versions!**
|
||||
* File-based SDMMC backend support (from SD)
|
||||
* File-based SDMMC backend support (from SD)
|
||||
**This allows loading eMMC images from hekate-backups (split or not)**
|
||||
* SDMMC device based sector offset (*currently eMMC only*)
|
||||
* SDMMC device based sector offset (*currently eMMC only*)
|
||||
**Raw partition support for eMMC from SD with less performance overhead**
|
||||
* Full support for `/Nintendo` folder redirection to a arbitrary path
|
||||
* Full support for `/Nintendo` folder redirection to a arbitrary path
|
||||
**No 8 char length restriction!**
|
||||
* exosphere based context configuration
|
||||
* exosphere based context configuration
|
||||
**This includes full support for multiple emuMMC images**
|
||||
|
||||
## Compiling
|
||||
|
||||
8
emummc/source/FS/FS_offsets.c
vendored
8
emummc/source/FS/FS_offsets.c
vendored
@@ -57,6 +57,8 @@
|
||||
#include "offsets/1203_exfat.h"
|
||||
#include "offsets/1300.h"
|
||||
#include "offsets/1300_exfat.h"
|
||||
#include "offsets/1310.h"
|
||||
#include "offsets/1310_exfat.h"
|
||||
#include "../utils/fatal.h"
|
||||
|
||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
||||
@@ -125,6 +127,8 @@ DEFINE_OFFSET_STRUCT(_1203);
|
||||
DEFINE_OFFSET_STRUCT(_1203_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1300);
|
||||
DEFINE_OFFSET_STRUCT(_1300_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1310);
|
||||
DEFINE_OFFSET_STRUCT(_1310_EXFAT);
|
||||
|
||||
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
switch (version) {
|
||||
@@ -210,6 +214,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1300));
|
||||
case FS_VER_13_0_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1300_EXFAT));
|
||||
case FS_VER_13_1_0:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1310));
|
||||
case FS_VER_13_1_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1310_EXFAT));
|
||||
default:
|
||||
fatal_abort(Fatal_UnknownVersion);
|
||||
}
|
||||
|
||||
3
emummc/source/FS/FS_versions.h
vendored
3
emummc/source/FS/FS_versions.h
vendored
@@ -83,6 +83,9 @@ enum FS_VER
|
||||
FS_VER_13_0_0,
|
||||
FS_VER_13_0_0_EXFAT,
|
||||
|
||||
FS_VER_13_1_0,
|
||||
FS_VER_13_1_0_EXFAT,
|
||||
|
||||
FS_VER_MAX,
|
||||
};
|
||||
|
||||
|
||||
59
emummc/source/FS/offsets/1310.h
vendored
Normal file
59
emummc/source/FS/offsets/1310.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_1310_H__
|
||||
#define __FS_1310_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1310_SDMMC_ACCESSOR_GC 0x158C20
|
||||
#define FS_OFFSET_1310_SDMMC_ACCESSOR_SD 0x15AA30
|
||||
#define FS_OFFSET_1310_SDMMC_ACCESSOR_NAND 0x159150
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1310_SDMMC_WRAPPER_READ 0x1545C0
|
||||
#define FS_OFFSET_1310_SDMMC_WRAPPER_WRITE 0x154680
|
||||
#define FS_OFFSET_1310_RTLD 0x688
|
||||
#define FS_OFFSET_1310_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1310_CLKRST_SET_MIN_V_CLK_RATE 0x1537C0
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1310_LOCK_MUTEX 0x29690
|
||||
#define FS_OFFSET_1310_UNLOCK_MUTEX 0x296E0
|
||||
|
||||
#define FS_OFFSET_1310_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1544A0
|
||||
#define FS_OFFSET_1310_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x154530
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1310_SD_MUTEX 0xE133E8
|
||||
#define FS_OFFSET_1310_NAND_MUTEX 0xE0E768
|
||||
#define FS_OFFSET_1310_ACTIVE_PARTITION 0xE0E7A8
|
||||
#define FS_OFFSET_1310_SDMMC_DAS_HANDLE 0xDF6E18
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1310_SD_DAS_INIT 0x27744
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1310_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006EBE0, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0007BEEC, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00082294, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0009422C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1310_H__
|
||||
59
emummc/source/FS/offsets/1310_exfat.h
vendored
Normal file
59
emummc/source/FS/offsets/1310_exfat.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_1310_EXFAT_H__
|
||||
#define __FS_1310_EXFAT_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_ACCESSOR_GC 0x158C20
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_ACCESSOR_SD 0x15AA30
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_ACCESSOR_NAND 0x159150
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_WRAPPER_READ 0x1545C0
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_WRAPPER_WRITE 0x154680
|
||||
#define FS_OFFSET_1310_EXFAT_RTLD 0x688
|
||||
#define FS_OFFSET_1310_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1310_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1537C0
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1310_EXFAT_LOCK_MUTEX 0x29690
|
||||
#define FS_OFFSET_1310_EXFAT_UNLOCK_MUTEX 0x296E0
|
||||
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1544A0
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x154530
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1310_EXFAT_SD_MUTEX 0xE203E8
|
||||
#define FS_OFFSET_1310_EXFAT_NAND_MUTEX 0xE1B768
|
||||
#define FS_OFFSET_1310_EXFAT_ACTIVE_PARTITION 0xE1B7A8
|
||||
#define FS_OFFSET_1310_EXFAT_SDMMC_DAS_HANDLE 0xE03E18
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1310_EXFAT_SD_DAS_INIT 0x27744
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1310_EXFAT_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006EBE0, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0007BEEC, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00082294, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0009422C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1310_EXFAT_H__
|
||||
@@ -150,6 +150,9 @@ namespace ams::nxboot {
|
||||
FsVersion_13_0_0,
|
||||
FsVersion_13_0_0_Exfat,
|
||||
|
||||
FsVersion_13_1_0,
|
||||
FsVersion_13_1_0_Exfat,
|
||||
|
||||
FsVersion_Count,
|
||||
};
|
||||
|
||||
@@ -215,6 +218,9 @@ namespace ams::nxboot {
|
||||
|
||||
{ 0x7D, 0x20, 0x05, 0x47, 0x17, 0x8A, 0x83, 0x6A }, /* FsVersion_13_0_0 */
|
||||
{ 0x51, 0xEB, 0xFA, 0x9C, 0xCF, 0x66, 0xC0, 0x9E }, /* FsVersion_13_0_0_Exfat */
|
||||
|
||||
{ 0x91, 0xBA, 0x65, 0xA2, 0x1C, 0x1D, 0x50, 0xAE }, /* FsVersion_13_1_0 */
|
||||
{ 0x76, 0x38, 0x27, 0xEE, 0x9C, 0x20, 0x7E, 0x5B }, /* FsVersion_13_1_0_Exfat */
|
||||
};
|
||||
|
||||
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
|
||||
@@ -603,6 +609,11 @@ namespace ams::nxboot {
|
||||
AddPatch(fs_meta, 0x159119, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x1426D0, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
case FsVersion_13_1_0:
|
||||
case FsVersion_13_1_0_Exfat:
|
||||
AddPatch(fs_meta, 0x1590B9, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x142670, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = cf765c0946cc5c828364ae6bfccddc4041304f28
|
||||
parent = 8634ea0f7c4f0e68adf2dfaaddc6ae1e225c4fc2
|
||||
commit = ceff2f3712d66904747f77619cd6dc12a5dcea8b
|
||||
parent = 6cf5205a28cfeb30930fd1cfd3c8369cd13edfa3
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
11
libraries/config/arch/armv4t/arch.mk
Normal file
11
libraries/config/arch/armv4t/arch.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/devkitARM/base_rules
|
||||
|
||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V4T
|
||||
export ATMOSPHERE_SETTINGS +=
|
||||
export ATMOSPHERE_CFLAGS +=
|
||||
export ATMOSPHERE_CXXFLAGS +=
|
||||
export ATMOSPHERE_ASFLAGS +=
|
||||
11
libraries/config/arch/armv8a/arch.mk
Normal file
11
libraries/config/arch/armv8a/arch.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/devkitA64/base_rules
|
||||
|
||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM_V8A
|
||||
export ATMOSPHERE_SETTINGS +=
|
||||
export ATMOSPHERE_CFLAGS +=
|
||||
export ATMOSPHERE_CXXFLAGS +=
|
||||
export ATMOSPHERE_ASFLAGS +=
|
||||
@@ -39,6 +39,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
|
||||
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
||||
export ATMOSPHERE_OS_NAME := horizon
|
||||
|
||||
export ATMOSPHERE_SUB_ARCH_DIR = armv8a
|
||||
export ATMOSPHERE_SUB_ARCH_NAME = armv8a
|
||||
|
||||
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
|
||||
else ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
|
||||
export ATMOSPHERE_ARCH_DIR := arm
|
||||
@@ -49,6 +52,9 @@ export ATMOSPHERE_ARCH_NAME := arm
|
||||
export ATMOSPHERE_BOARD_NAME := nintendo_nx
|
||||
export ATMOSPHERE_OS_NAME := horizon
|
||||
|
||||
export ATMOSPHERE_SUB_ARCH_DIR = armv4t
|
||||
export ATMOSPHERE_SUB_ARCH_NAME = armv4t
|
||||
|
||||
export ATMOSPHERE_CPU_EXTENSIONS :=
|
||||
endif
|
||||
|
||||
@@ -64,6 +70,9 @@ export ATMOSPHERE_ARCH_NAME := arm64
|
||||
export ATMOSPHERE_BOARD_NAME := qemu_virt
|
||||
export ATMOSPHERE_OS_NAME := horizon
|
||||
|
||||
export ATMOSPHERE_SUB_ARCH_DIR = armv8a
|
||||
export ATMOSPHERE_SUB_ARCH_NAME = armv8a
|
||||
|
||||
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
|
||||
endif
|
||||
|
||||
@@ -85,14 +94,27 @@ export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/board/$(ATMOSP
|
||||
export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/os/$(ATMOSPHERE_OS_DIR)
|
||||
export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_ARCH_MAKE_DIR)/cpu/$(ATMOSPHERE_CPU_DIR)
|
||||
|
||||
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
|
||||
export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(ATMOSPHERE_SUB_ARCH_NAME)
|
||||
export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(ATMOSPHERE_SUB_ARCH_NAME)
|
||||
else
|
||||
export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
|
||||
export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
|
||||
endif
|
||||
|
||||
include $(ATMOSPHERE_ARCH_MAKE_DIR)/arch.mk
|
||||
include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk
|
||||
include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk
|
||||
include $(ATMOSPHERE_CPU_MAKE_DIR)/cpu.mk
|
||||
|
||||
|
||||
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
|
||||
export ATMOSPHERE_SUB_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/arch/$(ATMOSPHERE_SUB_ARCH_DIR)
|
||||
|
||||
include $(ATMOSPHERE_SUB_ARCH_MAKE_DIR)/arch.mk
|
||||
endif
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# get atmosphere git revision information
|
||||
#---------------------------------------------------------------------------------
|
||||
@@ -125,12 +147,20 @@ DATA := data
|
||||
INCLUDES := include
|
||||
|
||||
GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1/os $1/cpu $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(filter-out $d,$(call GENERAL_SOURCE_DIRS,$d)) $d,))
|
||||
|
||||
SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),))
|
||||
|
||||
ifneq ($(strip $(ATMOSPHERE_SUB_ARCH_NAME)),)
|
||||
SPECIFIC_SOURCE_DIRS_ARCH=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/$4/.*),$1/$2/$4 $(call DIR_WILDCARD,$1/$2/$4),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),)))
|
||||
else
|
||||
SPECIFIC_SOURCE_DIRS_ARCH=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),))
|
||||
endif
|
||||
|
||||
UNFILTERED_SOURCE_DIRS=$1 $(foreach d,$(wildcard $1/*),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,))
|
||||
|
||||
ALL_SOURCE_DIRS=$(foreach d,$(call GENERAL_SOURCE_DIRS,$1), \
|
||||
$d \
|
||||
$(call SPECIFIC_SOURCE_DIRS,$d,arch,$(ATMOSPHERE_ARCH_DIR)) \
|
||||
$(call SPECIFIC_SOURCE_DIRS_ARCH,$d,arch,$(ATMOSPHERE_ARCH_DIR),$(ATMOSPHERE_SUB_ARCH_DIR)) \
|
||||
$(call SPECIFIC_SOURCE_DIRS,$d,board,$(ATMOSPHERE_BOARD_DIR)) \
|
||||
$(call SPECIFIC_SOURCE_DIRS,$d,os,$(ATMOSPHERE_OS_DIR)) \
|
||||
$(call SPECIFIC_SOURCE_DIRS,$d,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_DIR)) \
|
||||
@@ -147,7 +177,7 @@ FIND_SOURCE_FILES=$(foreach dir,$1,$(filter-out $(notdir $(wildcard $(dir)/*.arc
|
||||
$(notdir $(wildcard $(dir)/*.os.*.$2)) \
|
||||
$(notdir $(wildcard $(dir)/.cpu.*.$2)), \
|
||||
$(notdir $(wildcard $(dir)/*.$2)))) \
|
||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),arch,$(ATMOSPHERE_ARCH_NAME),$2)) \
|
||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),arch,$(ATMOSPHERE_ARCH_NAME) $(ATMOSPHERE_SUB_ARCH_NAME),$2)) \
|
||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),board,$(ATMOSPHERE_BOARD_NAME),$2)) \
|
||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),os,$(ATMOSPHERE_OS_NAME),$2)) \
|
||||
$(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),cpu,$(ATMOSPHERE_CPU_NAME) $(ATMOSPHERE_CPU_EXTENSIONS),$2))
|
||||
|
||||
@@ -93,3 +93,4 @@
|
||||
|
||||
/* Deferred includes. */
|
||||
#include <mesosphere/kern_k_auto_object_impls.hpp>
|
||||
#include <mesosphere/kern_k_scheduler_impls.hpp>
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace ams::kern::arch::arm64::init {
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
cpu::InvalidateEntireTlb();
|
||||
cpu::InvalidateEntireTlbInnerShareable();
|
||||
|
||||
/* Copy data, if we should. */
|
||||
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
|
||||
@@ -350,7 +350,6 @@ namespace ams::kern::arch::arm64::init {
|
||||
/* If we don't already have an L2 table, we need to make a new one. */
|
||||
if (!l1_entry->IsTable()) {
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||
ClearNewPageTable(new_table);
|
||||
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
@@ -361,12 +360,12 @@ namespace ams::kern::arch::arm64::init {
|
||||
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
|
||||
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
|
||||
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
virt_addr += L2BlockSize;
|
||||
phys_addr += L2BlockSize;
|
||||
size -= L2BlockSize;
|
||||
}
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -384,7 +383,6 @@ namespace ams::kern::arch::arm64::init {
|
||||
/* If we don't already have an L3 table, we need to make a new one. */
|
||||
if (!l2_entry->IsTable()) {
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||
ClearNewPageTable(new_table);
|
||||
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
@@ -395,12 +393,12 @@ namespace ams::kern::arch::arm64::init {
|
||||
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
|
||||
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
|
||||
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
virt_addr += L3BlockSize;
|
||||
phys_addr += L3BlockSize;
|
||||
size -= L3BlockSize;
|
||||
}
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/* TODO: Different header for this? */
|
||||
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
|
||||
|
||||
/* ams::kern::KThread, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
||||
#define THREAD_THREAD_CONTEXT 0xD0
|
||||
|
||||
/* ams::kern::KThread::StackParameters, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
||||
#define THREAD_STACK_PARAMETERS_SIZE 0x30
|
||||
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
||||
|
||||
@@ -52,10 +52,19 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
__asm__ __volatile__("dmb sy" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void DataMemoryBarrierInnerShareable() {
|
||||
__asm__ __volatile__("dmb ish" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InstructionMemoryBarrier() {
|
||||
__asm__ __volatile__("isb" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void EnsureInstructionConsistencyInnerShareable() {
|
||||
DataSynchronizationBarrierInnerShareable();
|
||||
InstructionMemoryBarrier();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void EnsureInstructionConsistency() {
|
||||
DataSynchronizationBarrier();
|
||||
InstructionMemoryBarrier();
|
||||
@@ -173,7 +182,6 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
NOINLINE void SynchronizeAllCores();
|
||||
|
||||
/* Cache management helpers. */
|
||||
void ClearPageToZeroImpl(void *);
|
||||
void StoreEntireCacheForInit();
|
||||
void FlushEntireCacheForInit();
|
||||
|
||||
@@ -186,10 +194,16 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
void InvalidateEntireInstructionCache();
|
||||
|
||||
ALWAYS_INLINE void ClearPageToZero(void *page) {
|
||||
ALWAYS_INLINE void ClearPageToZero(void * const page) {
|
||||
MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
|
||||
MESOSPHERE_ASSERT(page != nullptr);
|
||||
ClearPageToZeroImpl(page);
|
||||
|
||||
uintptr_t cur = reinterpret_cast<uintptr_t>(__builtin_assume_aligned(page, PageSize));
|
||||
const uintptr_t last = cur + PageSize - DataCacheLineSize;
|
||||
|
||||
for (/* ... */; cur <= last; cur += DataCacheLineSize) {
|
||||
__asm__ __volatile__("dc zva, %[cur]" :: [cur]"r"(cur) : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
|
||||
@@ -209,6 +223,11 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateEntireTlbInnerShareable() {
|
||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||
EnsureInstructionConsistencyInnerShareable();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||
DataSynchronizationBarrier();
|
||||
|
||||
@@ -105,35 +105,48 @@ namespace ams::kern::arch::arm64 {
|
||||
Result UnbindLocal(s32 irq);
|
||||
Result ClearGlobal(s32 irq);
|
||||
Result ClearLocal(s32 irq);
|
||||
public:
|
||||
static ALWAYS_INLINE u32 DisableInterrupts() {
|
||||
private:
|
||||
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledState() {
|
||||
u64 intr_state;
|
||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
||||
"msr daifset, #2"
|
||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
||||
"ubfx %[intr_state], %[intr_state], #7, #1"
|
||||
: [intr_state]"=r"(intr_state)
|
||||
:: "memory");
|
||||
return intr_state;
|
||||
}
|
||||
public:
|
||||
static ALWAYS_INLINE void EnableInterrupts() {
|
||||
__asm__ __volatile__("msr daifclr, #2" ::: "memory");
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE u32 EnableInterrupts() {
|
||||
u64 intr_state;
|
||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
||||
"msr daifclr, #2"
|
||||
: [intr_state]"=r"(intr_state)
|
||||
:: "memory");
|
||||
static ALWAYS_INLINE void DisableInterrupts() {
|
||||
__asm__ __volatile__("msr daifset, #2" ::: "memory");
|
||||
}
|
||||
|
||||
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndDisableInterrupts() {
|
||||
const auto intr_state = GetInterruptsEnabledState();
|
||||
DisableInterrupts();
|
||||
return intr_state;
|
||||
}
|
||||
|
||||
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndEnableInterrupts() {
|
||||
const auto intr_state = GetInterruptsEnabledState();
|
||||
EnableInterrupts();
|
||||
return intr_state;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
|
||||
u64 cur_state;
|
||||
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
|
||||
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
|
||||
u64 tmp;
|
||||
__asm__ __volatile__("mrs %[tmp], daif\n"
|
||||
"bfi %[tmp], %[intr_state], #7, #1\n"
|
||||
"msr daif, %[tmp]"
|
||||
: [tmp]"=&r"(tmp)
|
||||
: [intr_state]"r"(intr_state)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE bool AreInterruptsEnabled() {
|
||||
u64 intr_state;
|
||||
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
|
||||
return (intr_state & 0x80) == 0;
|
||||
return GetInterruptsEnabledState() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -219,27 +219,27 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
||||
static void PteDataSynchronizationBarrier() {
|
||||
static ALWAYS_INLINE void PteDataSynchronizationBarrier() {
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
static void ClearPageTable(KVirtualAddress table) {
|
||||
static ALWAYS_INLINE void ClearPageTable(KVirtualAddress table) {
|
||||
cpu::ClearPageToZero(GetVoidPointer(table));
|
||||
}
|
||||
|
||||
void OnTableUpdated() const {
|
||||
ALWAYS_INLINE void OnTableUpdated() const {
|
||||
cpu::InvalidateTlbByAsid(m_asid);
|
||||
}
|
||||
|
||||
void OnKernelTableUpdated() const {
|
||||
ALWAYS_INLINE void OnKernelTableUpdated() const {
|
||||
cpu::InvalidateEntireTlbDataOnly();
|
||||
}
|
||||
|
||||
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
||||
ALWAYS_INLINE void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
||||
cpu::InvalidateTlbByVaDataOnly(virt_addr);
|
||||
}
|
||||
|
||||
void NoteUpdated() const {
|
||||
ALWAYS_INLINE void NoteUpdated() const {
|
||||
cpu::DataSynchronizationBarrier();
|
||||
|
||||
if (this->IsKernel()) {
|
||||
@@ -249,7 +249,7 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
}
|
||||
|
||||
void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
|
||||
ALWAYS_INLINE void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
|
||||
MESOSPHERE_ASSERT(this->IsKernel());
|
||||
|
||||
cpu::DataSynchronizationBarrier();
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
/* Select L1 cache. */
|
||||
cpu::SetCsselrEl1(0);
|
||||
cpu::InstructionMemoryBarrier();
|
||||
|
||||
/* Check that the L1 cache is not direct-mapped. */
|
||||
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
/* of the right side, and so this does not actually cost any space. */
|
||||
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST
|
||||
|
||||
/* NOTE: This enables usage of KDebug handles as parameter for svc::GetInfo */
|
||||
/* calls which require a process parameter. This enables a debugger to obtain */
|
||||
/* address space/layout information, for example. However, it changes abi, and so */
|
||||
/* this define allows toggling the extension. */
|
||||
#define MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS
|
||||
|
||||
/* NOTE: This uses currently-reserved bits inside the MapRange capability */
|
||||
/* in order to support large physical addresses (40-bit instead of 36). */
|
||||
/* This is toggleable in order to disable it if N ever uses those bits. */
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace ams::kern {
|
||||
return m_slab_heap->Allocate(m_page_allocator);
|
||||
}
|
||||
|
||||
void Free(T *t) const {
|
||||
ALWAYS_INLINE void Free(T *t) const {
|
||||
m_slab_heap->Free(t);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -211,18 +211,6 @@ namespace ams::kern {
|
||||
static consteval bool ValidateAssemblyOffsets();
|
||||
};
|
||||
|
||||
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_runnable) == KSCHEDULER_INTERRUPT_TASK_RUNNABLE);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.prev_thread) == KSCHEDULER_PREVIOUS_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_manager) == KSCHEDULER_INTERRUPT_TASK_MANAGER);
|
||||
|
||||
return true;
|
||||
}
|
||||
static_assert(KScheduler::ValidateAssemblyOffsets());
|
||||
|
||||
class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> {
|
||||
public:
|
||||
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 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_scheduler.hpp>
|
||||
#include <mesosphere/kern_select_interrupt_manager.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
/* NOTE: This header is included after all main headers. */
|
||||
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_runnable) == KSCHEDULER_INTERRUPT_TASK_RUNNABLE);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.prev_thread) == KSCHEDULER_PREVIOUS_THREAD);
|
||||
static_assert(AMS_OFFSETOF(KScheduler, m_state.interrupt_task_manager) == KSCHEDULER_INTERRUPT_TASK_MANAGER);
|
||||
|
||||
return true;
|
||||
}
|
||||
static_assert(KScheduler::ValidateAssemblyOffsets());
|
||||
|
||||
ALWAYS_INLINE void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
|
||||
if (const u64 core_mask = cores_needing_scheduling & ~(1ul << m_core_id); core_mask != 0) {
|
||||
cpu::DataSynchronizationBarrier();
|
||||
Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_Scheduler, core_mask);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,6 +74,9 @@ namespace ams::kern {
|
||||
|
||||
/* Release an instance of the lock. */
|
||||
if ((--m_lock_count) == 0) {
|
||||
/* Perform a memory barrier here. */
|
||||
cpu::DataMemoryBarrierInnerShareable();
|
||||
|
||||
/* We're no longer going to hold the lock. Take note of what cores need scheduling. */
|
||||
const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads();
|
||||
|
||||
|
||||
@@ -405,8 +405,6 @@ namespace ams::kern {
|
||||
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
||||
constexpr ThreadState GetRawState() const { return m_thread_state; }
|
||||
|
||||
NOINLINE KThreadContext *GetContextForSchedulerLoop();
|
||||
|
||||
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
|
||||
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
||||
|
||||
@@ -624,9 +622,7 @@ namespace ams::kern {
|
||||
void OnTimer();
|
||||
void DoWorkerTaskImpl();
|
||||
public:
|
||||
static constexpr bool IsConditionVariableThreadTreeValid() {
|
||||
return ConditionVariableThreadTreeTraits::IsValid();
|
||||
}
|
||||
static consteval bool IsKThreadStructurallyValid();
|
||||
|
||||
static KThread *GetThreadFromId(u64 thread_id);
|
||||
static Result GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count);
|
||||
@@ -634,7 +630,18 @@ namespace ams::kern {
|
||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||
};
|
||||
static_assert(alignof(KThread) == 0x10);
|
||||
static_assert(KThread::IsConditionVariableThreadTreeValid());
|
||||
|
||||
consteval bool KThread::IsKThreadStructurallyValid() {
|
||||
/* Check that the condition variable tree is valid. */
|
||||
static_assert(ConditionVariableThreadTreeTraits::IsValid());
|
||||
|
||||
/* Check that the assembly offsets are valid. */
|
||||
static_assert(AMS_OFFSETOF(KThread, m_thread_context) == THREAD_THREAD_CONTEXT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(KThread::IsKThreadStructurallyValid());
|
||||
|
||||
class KScopedDisableDispatch {
|
||||
public:
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ams::kern {
|
||||
private:
|
||||
u32 m_prev_intr_state;
|
||||
public:
|
||||
ALWAYS_INLINE KScopedInterruptDisable() : m_prev_intr_state(KInterruptManager::DisableInterrupts()) { /* ... */ }
|
||||
ALWAYS_INLINE KScopedInterruptDisable() : m_prev_intr_state(KInterruptManager::GetInterruptsEnabledStateAndDisableInterrupts()) { /* ... */ }
|
||||
ALWAYS_INLINE ~KScopedInterruptDisable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
||||
private:
|
||||
u32 m_prev_intr_state;
|
||||
public:
|
||||
ALWAYS_INLINE KScopedInterruptEnable() : m_prev_intr_state(KInterruptManager::EnableInterrupts()) { /* ... */ }
|
||||
ALWAYS_INLINE KScopedInterruptEnable() : m_prev_intr_state(KInterruptManager::GetInterruptsEnabledStateAndEnableInterrupts()) { /* ... */ }
|
||||
ALWAYS_INLINE ~KScopedInterruptEnable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
} else {
|
||||
m_counter = cpu::GetPerformanceCounter(m_which);
|
||||
}
|
||||
DataMemoryBarrier();
|
||||
DataMemoryBarrierInnerShareable();
|
||||
m_done = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -61,139 +61,3 @@ _ZN3ams4kern4arch5arm643cpu23SynchronizeAllCoresImplEPii:
|
||||
5:
|
||||
stlr wzr, [x0]
|
||||
ret
|
||||
|
||||
|
||||
/* ams::kern::arch::arm64::cpu::ClearPageToZero(void *) */
|
||||
.section .text._ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv, "ax", %progbits
|
||||
.global _ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv
|
||||
.type _ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv, %function
|
||||
_ZN3ams4kern4arch5arm643cpu19ClearPageToZeroImplEPv:
|
||||
/* Efficiently clear the page using dc zva. */
|
||||
dc zva, x0
|
||||
add x8, x0, #0x040
|
||||
dc zva, x8
|
||||
add x8, x0, #0x080
|
||||
dc zva, x8
|
||||
add x8, x0, #0x0c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x100
|
||||
dc zva, x8
|
||||
add x8, x0, #0x140
|
||||
dc zva, x8
|
||||
add x8, x0, #0x180
|
||||
dc zva, x8
|
||||
add x8, x0, #0x1c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x200
|
||||
dc zva, x8
|
||||
add x8, x0, #0x240
|
||||
dc zva, x8
|
||||
add x8, x0, #0x280
|
||||
dc zva, x8
|
||||
add x8, x0, #0x2c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x300
|
||||
dc zva, x8
|
||||
add x8, x0, #0x340
|
||||
dc zva, x8
|
||||
add x8, x0, #0x380
|
||||
dc zva, x8
|
||||
add x8, x0, #0x3c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x400
|
||||
dc zva, x8
|
||||
add x8, x0, #0x440
|
||||
dc zva, x8
|
||||
add x8, x0, #0x480
|
||||
dc zva, x8
|
||||
add x8, x0, #0x4c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x500
|
||||
dc zva, x8
|
||||
add x8, x0, #0x540
|
||||
dc zva, x8
|
||||
add x8, x0, #0x580
|
||||
dc zva, x8
|
||||
add x8, x0, #0x5c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x600
|
||||
dc zva, x8
|
||||
add x8, x0, #0x640
|
||||
dc zva, x8
|
||||
add x8, x0, #0x680
|
||||
dc zva, x8
|
||||
add x8, x0, #0x6c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x700
|
||||
dc zva, x8
|
||||
add x8, x0, #0x740
|
||||
dc zva, x8
|
||||
add x8, x0, #0x780
|
||||
dc zva, x8
|
||||
add x8, x0, #0x7c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x800
|
||||
dc zva, x8
|
||||
add x8, x0, #0x840
|
||||
dc zva, x8
|
||||
add x8, x0, #0x880
|
||||
dc zva, x8
|
||||
add x8, x0, #0x8c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0x900
|
||||
dc zva, x8
|
||||
add x8, x0, #0x940
|
||||
dc zva, x8
|
||||
add x8, x0, #0x980
|
||||
dc zva, x8
|
||||
add x8, x0, #0x9c0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xa00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xa40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xa80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xac0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xb00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xb40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xb80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xbc0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xc00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xc40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xc80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xcc0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xd00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xd40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xd80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xdc0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xe00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xe40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xe80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xec0
|
||||
dc zva, x8
|
||||
add x8, x0, #0xf00
|
||||
dc zva, x8
|
||||
add x8, x0, #0xf40
|
||||
dc zva, x8
|
||||
add x8, x0, #0xf80
|
||||
dc zva, x8
|
||||
add x8, x0, #0xfc0
|
||||
dc zva, x8
|
||||
ret
|
||||
@@ -225,7 +225,7 @@ namespace ams::kern::arch::arm64 {
|
||||
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
|
||||
GetCurrentThread().ClearSingleStep();
|
||||
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
|
||||
cpu::EnsureInstructionConsistency();
|
||||
cpu::InstructionMemoryBarrier();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace ams::kern::arch::arm64 {
|
||||
if (user_mode) {
|
||||
KThread *cur_thread = GetCurrentThreadPointer();
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
KScopedInterruptEnable ei;
|
||||
EnableInterrupts();
|
||||
cur_thread->Exit();
|
||||
}
|
||||
}
|
||||
@@ -212,13 +212,14 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
|
||||
if (KInterruptController::IsGlobal(irq)) {
|
||||
KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
|
||||
} else {
|
||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
return this->BindLocal(handler, irq, priority, manual_clear);
|
||||
}
|
||||
}
|
||||
@@ -228,13 +229,16 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
|
||||
if (KInterruptController::IsGlobal(irq)) {
|
||||
KScopedInterruptDisable di;
|
||||
|
||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||
return this->UnbindGlobal(irq);
|
||||
} else {
|
||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
return this->UnbindLocal(irq);
|
||||
}
|
||||
}
|
||||
@@ -244,13 +248,15 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
|
||||
if (KInterruptController::IsGlobal(irq)) {
|
||||
KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||
return this->ClearGlobal(irq);
|
||||
} else {
|
||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||
|
||||
KScopedInterruptDisable di;
|
||||
return this->ClearLocal(irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,10 +169,10 @@ namespace ams::kern::arch::arm64 {
|
||||
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
||||
|
||||
/* Allocate a page for ttbr. */
|
||||
/* NOTE: It is a postcondition of page table manager allocation that the page is all-zero. */
|
||||
const u64 asid_tag = (static_cast<u64>(m_asid) << 48ul);
|
||||
const KVirtualAddress page = m_manager->Allocate();
|
||||
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
|
||||
cpu::ClearPageToZero(GetVoidPointer(page));
|
||||
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
|
||||
|
||||
/* Initialize the base page table. */
|
||||
@@ -1058,7 +1058,7 @@ namespace ams::kern::arch::arm64 {
|
||||
auto sw_reserved_bits = PageTableEntry::EncodeSoftwareReservedBits(head_entry->IsHeadMergeDisabled(), head_entry->IsHeadAndBodyMergeDisabled(), tail_entry->IsTailMergeDisabled());
|
||||
|
||||
/* Merge! */
|
||||
PteDataSynchronizationBarrier();
|
||||
/* NOTE: As of 13.1.0, Nintendo does not do: PteDataSynchronizationBarrier(); */
|
||||
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), sw_reserved_bits, false);
|
||||
|
||||
/* Note that we updated. */
|
||||
|
||||
@@ -28,7 +28,7 @@ _ZN3ams4kern3svc25CallReturnFromException64Ev:
|
||||
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
stp x26, x26, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||
|
||||
@@ -50,10 +50,6 @@ _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev:
|
||||
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
|
||||
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
|
||||
_ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
||||
/* Load x4-x7 from where the svc handler stores them. */
|
||||
ldp x4, x5, [sp, #(8 * 0)]
|
||||
ldp x6, x7, [sp, #(8 * 2)]
|
||||
|
||||
/* Allocate space for the light ipc data. */
|
||||
sub sp, sp, #(4 * 8)
|
||||
|
||||
@@ -78,13 +74,8 @@ _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
||||
/* Free the stack space for the light ipc data. */
|
||||
add sp, sp, #(4 * 8)
|
||||
|
||||
/* Save x4-x7 to where the svc handler stores them. */
|
||||
stp x4, x5, [sp, #(8 * 0)]
|
||||
stp x6, x7, [sp, #(8 * 2)]
|
||||
|
||||
ret
|
||||
|
||||
|
||||
/* ams::kern::svc::CallReplyAndReceiveLight64() */
|
||||
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
|
||||
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
|
||||
@@ -121,10 +112,6 @@ _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev:
|
||||
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
|
||||
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
|
||||
_ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
||||
/* Load x4-x7 from where the svc handler stores them. */
|
||||
ldp x4, x5, [sp, #(8 * 0)]
|
||||
ldp x6, x7, [sp, #(8 * 2)]
|
||||
|
||||
/* Allocate space for the light ipc data. */
|
||||
sub sp, sp, #(4 * 8)
|
||||
|
||||
@@ -149,8 +136,4 @@ _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
||||
/* Free the stack space for the light ipc data. */
|
||||
add sp, sp, #(4 * 8)
|
||||
|
||||
/* Save x4-x7 to where the svc handler stores them. */
|
||||
stp x4, x5, [sp, #(8 * 0)]
|
||||
stp x6, x7, [sp, #(8 * 2)]
|
||||
|
||||
ret
|
||||
|
||||
@@ -656,9 +656,8 @@ namespace ams::kern::board::nintendo::nx {
|
||||
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
||||
Kernel::GetSystemPageTableManager().Open(table_virt_addr, 1);
|
||||
|
||||
/* Clear the page and save it. */
|
||||
/* Save the page. Note that it is a pre-condition that the page is cleared, when allocated from the system page table manager. */
|
||||
/* NOTE: Nintendo does not check the result of StoreDataCache. */
|
||||
cpu::ClearPageToZero(GetVoidPointer(table_virt_addr));
|
||||
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
|
||||
g_reserved_table_phys_addr = table_phys_addr;
|
||||
|
||||
|
||||
@@ -341,7 +341,9 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* Restore pmu registers. */
|
||||
cpu::SetPmUserEnrEl0(0);
|
||||
cpu::PerformanceMonitorsControlRegisterAccessor().SetEventCounterReset(true).SetCycleCounterReset(true).Store();
|
||||
cpu::PerformanceMonitorsControlRegisterAccessor(0).SetEventCounterReset(true).SetCycleCounterReset(true).Store();
|
||||
cpu::EnsureInstructionConsistency();
|
||||
|
||||
cpu::SetPmOvsClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||
cpu::SetPmIntEnClrEl1(static_cast<u64>(static_cast<u32>(~u32())));
|
||||
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace ams::kern {
|
||||
{
|
||||
const u32 has_waiter_flag = 1;
|
||||
WriteToUser(key, std::addressof(has_waiter_flag));
|
||||
cpu::DataMemoryBarrier();
|
||||
cpu::DataMemoryBarrierInnerShareable();
|
||||
}
|
||||
|
||||
/* Write the value to userspace. */
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ams::kern {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::tuple<KMemoryState, const char *> MemoryStateNames[] = {
|
||||
constexpr const std::pair<KMemoryState, const char *> MemoryStateNames[] = {
|
||||
{KMemoryState_Free , "----- Free -----"},
|
||||
{KMemoryState_Io , "Io "},
|
||||
{KMemoryState_Static , "Static "},
|
||||
@@ -41,6 +41,7 @@ namespace ams::kern {
|
||||
{KMemoryState_Kernel , "Kernel "},
|
||||
{KMemoryState_GeneratedCode , "GeneratedCode "},
|
||||
{KMemoryState_CodeOut , "CodeOut "},
|
||||
{KMemoryState_Coverage , "Coverage "},
|
||||
};
|
||||
|
||||
constexpr const char *GetMemoryStateName(KMemoryState state) {
|
||||
|
||||
@@ -79,13 +79,6 @@ namespace ams::kern {
|
||||
RescheduleCurrentCore();
|
||||
}
|
||||
|
||||
void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
|
||||
if (const u64 core_mask = cores_needing_scheduling & ~(1ul << m_core_id); core_mask != 0) {
|
||||
cpu::DataSynchronizationBarrier();
|
||||
Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_Scheduler, core_mask);
|
||||
}
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThread(KThread *highest_thread) {
|
||||
if (KThread *prev_highest_thread = m_state.highest_priority_thread; AMS_LIKELY(prev_highest_thread != highest_thread)) {
|
||||
if (AMS_LIKELY(prev_highest_thread != nullptr)) {
|
||||
@@ -254,9 +247,24 @@ namespace ams::kern {
|
||||
|
||||
MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread);
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
/* Ensure the single-step bit in mdscr reflects the correct single-step state for the new thread. */
|
||||
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(next_thread->IsSingleStep()).Store();
|
||||
#endif
|
||||
|
||||
/* Switch the current process, if we're switching processes. */
|
||||
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
||||
KProcess::Switch(cur_process, next_process);
|
||||
} else {
|
||||
/* The single-step bit set up above requires an instruction synchronization barrier, to ensure */
|
||||
/* the state change takes before we actually perform a return which might break-to-step. */
|
||||
/* KProcess::Switch performs an isb incidentally, and so when we're changing process we */
|
||||
/* can piggy-back off of that isb to avoid unnecessarily emptying the pipeline twice. */
|
||||
/* However, this means that when we're switching to thread in a different process, */
|
||||
/* we must ensure that we still isb. In practice, gcc will deduplicate into a single isb. */
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
cpu::InstructionMemoryBarrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Set the new thread. */
|
||||
|
||||
@@ -1315,10 +1315,6 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
KThreadContext *KThread::GetContextForSchedulerLoop() {
|
||||
return std::addressof(this->GetContext());
|
||||
}
|
||||
|
||||
KThread *KThread::GetThreadFromId(u64 thread_id) {
|
||||
/* Lock the list. */
|
||||
KThread::ListAccessor accessor;
|
||||
|
||||
@@ -38,6 +38,80 @@ namespace ams::kern::svc {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetInfoImpl(u64 *out, ams::svc::InfoType info_type, KProcess *process) {
|
||||
switch (info_type) {
|
||||
case ams::svc::InfoType_CoreMask:
|
||||
*out = process->GetCoreMask();
|
||||
break;
|
||||
case ams::svc::InfoType_PriorityMask:
|
||||
*out = process->GetPriorityMask();
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionSize:
|
||||
*out = process->GetPageTable().GetAliasRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionSize:
|
||||
*out = process->GetPageTable().GetHeapRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_TotalMemorySize:
|
||||
*out = process->GetTotalUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedMemorySize:
|
||||
*out = process->GetUsedUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionSize:
|
||||
*out = process->GetPageTable().GetAliasCodeRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionSize:
|
||||
*out = process->GetPageTable().GetStackRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeTotal:
|
||||
*out = process->GetTotalSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeUsed:
|
||||
*out = process->GetUsedSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_ProgramId:
|
||||
*out = process->GetProgramId();
|
||||
break;
|
||||
case ams::svc::InfoType_UserExceptionContextAddress:
|
||||
*out = GetInteger(process->GetProcessLocalRegionAddress());
|
||||
break;
|
||||
case ams::svc::InfoType_TotalNonSystemMemorySize:
|
||||
*out = process->GetTotalNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedNonSystemMemorySize:
|
||||
*out = process->GetUsedNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_IsApplication:
|
||||
*out = process->IsApplication();
|
||||
break;
|
||||
case ams::svc::InfoType_FreeThreadCount:
|
||||
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
|
||||
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
*out = limit_value - current_value;
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||
switch (info_type) {
|
||||
case ams::svc::InfoType_CoreMask:
|
||||
@@ -66,77 +140,34 @@ namespace ams::kern::svc {
|
||||
|
||||
/* Get the process from its handle. */
|
||||
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(handle);
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS)
|
||||
/* If we the process is valid, use it. */
|
||||
if (process.IsNotNull()) {
|
||||
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
|
||||
}
|
||||
|
||||
/* Otherwise, as a mesosphere extension check if we were passed a usable KDebug. */
|
||||
KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject<KDebug>(handle);
|
||||
R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the process from the debug object. */
|
||||
/* TODO: ResultInvalidHandle()? */
|
||||
R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated());
|
||||
R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated());
|
||||
|
||||
/* Close the process when we're done. */
|
||||
ON_SCOPE_EXIT { debug->CloseProcess(); };
|
||||
|
||||
/* Return the info. */
|
||||
return GetInfoImpl(out, info_type, debug->GetProcessUnsafe());
|
||||
#else
|
||||
/* Verify that the process is valid. */
|
||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
switch (info_type) {
|
||||
case ams::svc::InfoType_CoreMask:
|
||||
*out = process->GetCoreMask();
|
||||
break;
|
||||
case ams::svc::InfoType_PriorityMask:
|
||||
*out = process->GetPriorityMask();
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AliasRegionSize:
|
||||
*out = process->GetPageTable().GetAliasRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_HeapRegionSize:
|
||||
*out = process->GetPageTable().GetHeapRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_TotalMemorySize:
|
||||
*out = process->GetTotalUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedMemorySize:
|
||||
*out = process->GetUsedUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_AslrRegionSize:
|
||||
*out = process->GetPageTable().GetAliasCodeRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionAddress:
|
||||
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||
break;
|
||||
case ams::svc::InfoType_StackRegionSize:
|
||||
*out = process->GetPageTable().GetStackRegionSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeTotal:
|
||||
*out = process->GetTotalSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_SystemResourceSizeUsed:
|
||||
*out = process->GetUsedSystemResourceSize();
|
||||
break;
|
||||
case ams::svc::InfoType_ProgramId:
|
||||
*out = process->GetProgramId();
|
||||
break;
|
||||
case ams::svc::InfoType_UserExceptionContextAddress:
|
||||
*out = GetInteger(process->GetProcessLocalRegionAddress());
|
||||
break;
|
||||
case ams::svc::InfoType_TotalNonSystemMemorySize:
|
||||
*out = process->GetTotalNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_UsedNonSystemMemorySize:
|
||||
*out = process->GetUsedNonSystemUserPhysicalMemorySize();
|
||||
break;
|
||||
case ams::svc::InfoType_IsApplication:
|
||||
*out = process->IsApplication();
|
||||
break;
|
||||
case ams::svc::InfoType_FreeThreadCount:
|
||||
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
|
||||
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
|
||||
*out = limit_value - current_value;
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
break;
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
/* Return the relevant info. */
|
||||
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_DebuggerAttached:
|
||||
|
||||
@@ -118,12 +118,12 @@ namespace ams::fssystem {
|
||||
|
||||
/* Copy API. */
|
||||
Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
|
||||
NX_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
|
||||
ALWAYS_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
|
||||
return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
|
||||
}
|
||||
|
||||
Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size);
|
||||
NX_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
|
||||
ALWAYS_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
|
||||
return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
|
||||
}
|
||||
|
||||
@@ -148,11 +148,11 @@ namespace ams::fssystem {
|
||||
Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||
Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path);
|
||||
|
||||
template<typename F>
|
||||
NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
|
||||
template<s64 RetryMilliSeconds = 100>
|
||||
ALWAYS_INLINE Result RetryFinitelyForTargetLocked(auto f) {
|
||||
/* Retry up to 10 times, 100ms between retries. */
|
||||
constexpr s32 MaxRetryCount = 10;
|
||||
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(100);
|
||||
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(RetryMilliSeconds);
|
||||
|
||||
s32 remaining_retries = MaxRetryCount;
|
||||
while (true) {
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace ams::socket {
|
||||
|
||||
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||
|
||||
s32 Socket(Family domain, Type type, Protocol protocol);
|
||||
s32 SocketExempt(Family domain, Type type, Protocol protocol);
|
||||
|
||||
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||
|
||||
@@ -57,4 +57,42 @@ namespace ams::socket {
|
||||
}
|
||||
};
|
||||
|
||||
class SystemConfigLightDefault : public Config {
|
||||
public:
|
||||
static constexpr size_t DefaultTcpInitialSendBufferSize = 16_KB;
|
||||
static constexpr size_t DefaultTcpInitialReceiveBufferSize = 16_KB;
|
||||
static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 0_KB;
|
||||
static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 0_KB;
|
||||
static constexpr size_t DefaultUdpSendBufferSize = 9_KB;
|
||||
static constexpr size_t DefaultUdpReceiveBufferSize = 42240;
|
||||
static constexpr auto DefaultSocketBufferEfficiency = 2;
|
||||
static constexpr auto DefaultConcurrency = 2;
|
||||
static constexpr size_t DefaultAllocatorPoolSize = 64_KB;
|
||||
|
||||
static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] {
|
||||
constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax));
|
||||
constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax));
|
||||
|
||||
return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||
}();
|
||||
|
||||
static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] {
|
||||
constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize);
|
||||
constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize);
|
||||
|
||||
return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||
}();
|
||||
public:
|
||||
constexpr SystemConfigLightDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency)
|
||||
: Config(mp, mp_sz, ap,
|
||||
DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize,
|
||||
DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax,
|
||||
DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize,
|
||||
DefaultSocketBufferEfficiency, c)
|
||||
{
|
||||
/* Mark as system. */
|
||||
m_system = true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ams::spl::impl {
|
||||
|
||||
Result AllocateAesKeySlot(s32 *out_keyslot);
|
||||
Result DeallocateAesKeySlot(s32 keyslot);
|
||||
Result TestAesKeySlot(s32 *out_index, s32 keyslot);
|
||||
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot);
|
||||
|
||||
os::SystemEvent *GetAesKeySlotAvailableEvent();
|
||||
|
||||
|
||||
@@ -188,6 +188,12 @@ namespace ams::boot2 {
|
||||
return enable_htc != 0;
|
||||
}
|
||||
|
||||
bool IsStandaloneGdbstubEnabled() {
|
||||
u8 enable_gdbstub = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub");
|
||||
return enable_gdbstub != 0;
|
||||
}
|
||||
|
||||
bool IsAtmosphereLogManagerEnabled() {
|
||||
/* If htc is enabled, ams log manager is enabled. */
|
||||
if (IsHtcEnabled()) {
|
||||
@@ -403,6 +409,9 @@ namespace ams::boot2 {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
|
||||
} else if (IsStandaloneGdbstubEnabled()) {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
|
||||
} else {
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
|
||||
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
|
||||
|
||||
@@ -89,9 +89,8 @@ namespace ams::fs {
|
||||
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||
|
||||
/* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */
|
||||
AMS_UNUSED(space_id, attribute);
|
||||
AMS_ABORT();
|
||||
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
|
||||
}
|
||||
|
||||
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
||||
|
||||
@@ -20,6 +20,8 @@ extern "C" {
|
||||
constinit u32 __nx_fs_num_sessions = 1;
|
||||
constinit u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
constinit bool __nx_fsdev_support_cwd = false;
|
||||
|
||||
extern int __system_argc;
|
||||
extern char** __system_argv;
|
||||
|
||||
|
||||
@@ -52,6 +52,26 @@ namespace ams::os::impl {
|
||||
/* Get the thread impl object from libnx. */
|
||||
ThreadImpl *thread_impl = ::threadGetSelf();
|
||||
|
||||
/* Hack around libnx's main thread, to ensure stratosphere thread type consistency. */
|
||||
{
|
||||
auto *tlr = reinterpret_cast<uintptr_t *>(svc::GetThreadLocalRegion());
|
||||
for (size_t i = sizeof(svc::ThreadLocalRegion) / sizeof(uintptr_t); i > 0; --i) {
|
||||
if (auto *candidate = reinterpret_cast<ThreadImpl *>(tlr[i - 1]); candidate == thread_impl) {
|
||||
ThreadImpl *embedded_thread = std::addressof(main_thread->thread_impl_storage);
|
||||
|
||||
*embedded_thread = *thread_impl;
|
||||
|
||||
if (embedded_thread->next) {
|
||||
embedded_thread->next->prev_next = std::addressof(embedded_thread->next);
|
||||
}
|
||||
|
||||
thread_impl = embedded_thread;
|
||||
tlr[i-1] = reinterpret_cast<uintptr_t>(thread_impl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the thread priority. */
|
||||
s32 horizon_priority;
|
||||
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace ams::osdbg::impl {
|
||||
static_assert(AMS_OFFSETOF(ThreadLocalRegionIlp32, tls) == 0x1C0);
|
||||
|
||||
struct LibnxThreadVars {
|
||||
static constexpr u32 Magic = util::FourCC<'!','T','V','$'>::Code;
|
||||
static constexpr u32 Magic = util::ReverseFourCC<'!','T','V','$'>::Code;
|
||||
|
||||
u32 magic;
|
||||
::Handle handle;
|
||||
@@ -69,11 +69,11 @@ namespace ams::osdbg::impl {
|
||||
volatile u16 disable_counter;
|
||||
volatile u16 interrupt_flag;
|
||||
u32 reserved0;
|
||||
u64 tls[(0x1E0 - 0x108) / sizeof(u64)];
|
||||
u64 tls[(0x200 - sizeof(LibnxThreadVars) - 0x108) / sizeof(u64)];
|
||||
LibnxThreadVars thread_vars;
|
||||
};
|
||||
static_assert(sizeof(ThreadLocalRegionLibnx) == sizeof(svc::ThreadLocalRegion));
|
||||
static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x1E0);
|
||||
static_assert(AMS_OFFSETOF(ThreadLocalRegionLibnx, thread_vars) == 0x200 - sizeof(LibnxThreadVars));
|
||||
|
||||
struct LibnxThreadEntryArgs {
|
||||
u64 t;
|
||||
|
||||
@@ -50,9 +50,9 @@ namespace ams::osdbg {
|
||||
} else {
|
||||
/* Special-case libnx threads. */
|
||||
if (thread_info->_thread_type_type == ThreadTypeType_Libnx) {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%010lx", reinterpret_cast<uintptr_t>(thread_info->_thread_type));
|
||||
} else {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%010lx", reinterpret_cast<uintptr_t>(thread_info->_thread_type));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace ams::socket::impl {
|
||||
|
||||
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||
|
||||
s32 Socket(Family domain, Type type, Protocol protocol);
|
||||
s32 SocketExempt(Family domain, Type type, Protocol protocol);
|
||||
|
||||
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||
|
||||
@@ -206,6 +206,8 @@ namespace ams::socket::impl {
|
||||
|
||||
/* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -422,6 +424,22 @@ namespace ams::socket::impl {
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 Socket(Family domain, Type type, Protocol protocol) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ABORT_UNLESS(IsInitialized());
|
||||
|
||||
/* Perform the call. */
|
||||
Errno error = Errno::ESuccess;
|
||||
int result = ::bsdSocket(static_cast<int>(domain), static_cast<int>(type), static_cast<int>(protocol));
|
||||
TranslateResultToBsdError(error, result);
|
||||
|
||||
if (result < 0) {
|
||||
socket::impl::SetLastError(error);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ABORT_UNLESS(IsInitialized());
|
||||
|
||||
@@ -74,6 +74,10 @@ namespace ams::socket {
|
||||
return impl::Shutdown(desc, how);
|
||||
}
|
||||
|
||||
s32 Socket(Family domain, Type type, Protocol protocol) {
|
||||
return impl::Socket(domain, type, protocol);
|
||||
}
|
||||
|
||||
s32 SocketExempt(Family domain, Type type, Protocol protocol) {
|
||||
return impl::SocketExempt(domain, type, protocol);
|
||||
}
|
||||
|
||||
@@ -729,9 +729,10 @@ namespace ams::spl::impl {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result TestAesKeySlot(s32 *out_index, s32 keyslot) {
|
||||
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot) {
|
||||
if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) {
|
||||
*out_index = keyslot;
|
||||
*out_index = keyslot;
|
||||
*out_virtual = false;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -740,7 +741,8 @@ namespace ams::spl::impl {
|
||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
|
||||
|
||||
*out_index = index;
|
||||
*out_index = index;
|
||||
*out_virtual = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
|
||||
constexpr const sm::ServiceName ServiceNameForSystemProcess = sm::ServiceName::Encode("sprof:sp");
|
||||
|
||||
constexpr inline size_t BgAgentSessionCountMax = 2;
|
||||
constexpr inline size_t SystemProcessSessionCountMax = 5;
|
||||
constexpr inline size_t SystemProcessSessionCountMax = 10;
|
||||
|
||||
constexpr inline size_t SessionCountMax = BgAgentSessionCountMax + SystemProcessSessionCountMax;
|
||||
|
||||
@@ -42,8 +42,8 @@ namespace ams::sprofile::srv {
|
||||
|
||||
struct ServerManagerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x0;
|
||||
static constexpr size_t MaxDomains = SessionCountMax; /* NOTE: Official is 3 */
|
||||
static constexpr size_t MaxDomainObjects = 16; /* NOTE: Official is 8 */
|
||||
static constexpr size_t MaxDomains = SessionCountMax; /* NOTE: Official is 9 */
|
||||
static constexpr size_t MaxDomainObjects = 16; /* NOTE: Official is 14 */
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = false;
|
||||
};
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "sprofile_srv_types.hpp"
|
||||
|
||||
#define AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, ImportProfile, (const sprofile::srv::ProfileDataForImportData &data), (data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, Commit, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, ImportMetadata, (const sprofile::srv::ProfileMetadataForImportMetadata &data), (data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, ImportProfile, (const sprofile::srv::ProfileDataForImportData &import), (import)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, Commit, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, ImportMetadata, (const sprofile::srv::ProfileMetadataForImportMetadata &import), (import)) \
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, IProfileImporter, AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO)
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetUnsigned64, (sf::Out<u64> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSigned32, (sf::Out<s32> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetUnsigned32, (sf::Out<u32> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetByte, (sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key))
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetByte, (sf::Out<u8> out, sprofile::Identifier profile, sprofile::Identifier key), (out, profile, key))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, IProfileReader, AMS_SPROFILE_I_PROFILE_READER_INTERFACE_INFO)
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
#include "sprofile_srv_i_profile_importer.hpp"
|
||||
|
||||
#define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 100, Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, ReadMetadata, (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg), (out_count, out, arg)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out<bool> out, sprofile::Identifier revision_key), (out, revision_key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset, (), ())
|
||||
AMS_SF_METHOD_INFO(C, H, 100, Result, OpenProfileImporter, (sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, GetImportableProfileUrls, (sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg), (out_count, out, arg)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 201, Result, IsUpdateNeeded, (sf::Out<bool> out, sprofile::Identifier revision_key), (out, revision_key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2000, Result, Reset, (), ())
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace ams::sprofile::srv {
|
||||
bool HasProfile(Identifier id0, Identifier id1) {
|
||||
/* Require that we have metadata. */
|
||||
if (m_metadata.has_value()) {
|
||||
for (auto i = 0u; i < m_metadata->num_entries; ++i) {
|
||||
for (auto i = 0u; i < std::min<size_t>(m_metadata->num_entries, util::size(m_metadata->entries)); ++i) {
|
||||
const auto &entry = m_metadata->entries[i];
|
||||
if (entry.identifier_0 == id0 && entry.identifier_1 == id1) {
|
||||
return true;
|
||||
@@ -63,7 +63,7 @@ namespace ams::sprofile::srv {
|
||||
m_revision_key = meta.revision_key;
|
||||
|
||||
/* Import all profiles. */
|
||||
for (auto i = 0u; i < meta.num_entries; ++i) {
|
||||
for (auto i = 0u; i < std::min<size_t>(meta.num_entries, util::size(meta.entries)); ++i) {
|
||||
const auto &import_entry = meta.entries[i];
|
||||
if (!this->HasProfile(import_entry.identifier_0, import_entry.identifier_1)) {
|
||||
m_importing_profiles[m_importing_count++] = import_entry.identifier_0;
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
|
||||
namespace ams::sprofile::srv {
|
||||
|
||||
Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
|
||||
return m_manager->ImportProfile(data);
|
||||
Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
|
||||
return m_manager->ImportProfile(import);
|
||||
}
|
||||
|
||||
Result ProfileImporterImpl::Commit() {
|
||||
return m_manager->Commit();
|
||||
}
|
||||
|
||||
Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
|
||||
return m_manager->ImportMetadata(data);
|
||||
Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
|
||||
return m_manager->ImportMetadata(import);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,10 +26,14 @@ namespace ams::sprofile::srv {
|
||||
ProfileManager *m_manager;
|
||||
public:
|
||||
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
|
||||
|
||||
~ProfileImporterImpl() {
|
||||
m_manager->CloseProfileImporter();
|
||||
}
|
||||
public:
|
||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
|
||||
Result Commit();
|
||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import);
|
||||
};
|
||||
static_assert(IsIProfileImporter<ProfileImporterImpl>);
|
||||
|
||||
|
||||
@@ -151,7 +151,17 @@ namespace ams::sprofile::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
|
||||
void ProfileManager::CloseProfileImporter() {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||
std::scoped_lock lk2(m_general_mutex);
|
||||
std::scoped_lock lk3(m_fs_mutex);
|
||||
|
||||
/* Close our importer. */
|
||||
m_profile_importer = util::nullopt;
|
||||
}
|
||||
|
||||
Result ProfileManager::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
@@ -159,25 +169,39 @@ namespace ams::sprofile::srv {
|
||||
/* Check that we have an importer. */
|
||||
R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
|
||||
|
||||
/* Check that the metadata we're importing is valid. */
|
||||
R_UNLESS(data.data.version == ProfileDataVersion, sprofile::ResultInvalidDataVersion());
|
||||
/* Check that the metadata we're importing is a valid version. */
|
||||
R_UNLESS(IsValidProfileFormatVersion(import.header.version), sprofile::ResultInvalidDataVersion());
|
||||
|
||||
/* Check that the metadata we're importing has a valid hash. */
|
||||
{
|
||||
crypto::Md5Generator md5;
|
||||
md5.Initialize();
|
||||
|
||||
md5.Update(std::addressof(import.header), sizeof(import.header));
|
||||
md5.Update(std::addressof(import.data), sizeof(import.data) - sizeof(import.data.entries[0]) * (util::size(import.data.entries) - std::min<size_t>(import.data.num_entries, util::size(import.data.entries))));
|
||||
|
||||
u8 hash[crypto::Md5Generator::HashSize];
|
||||
md5.GetHash(hash, sizeof(hash));
|
||||
|
||||
R_UNLESS(crypto::IsSameBytes(hash, import.hash, sizeof(hash)), sprofile::ResultInvalidDataHash());
|
||||
}
|
||||
|
||||
/* Succeed if we already have the profile. */
|
||||
R_SUCCEED_IF(m_profile_importer->HasProfile(data.identifier_0, data.identifier_1));
|
||||
R_SUCCEED_IF(m_profile_importer->HasProfile(import.header.identifier_0, import.header.identifier_1));
|
||||
|
||||
/* Check that we're importing the profile. */
|
||||
R_UNLESS(m_profile_importer->CanImportProfile(data.identifier_0), sprofile::ResultInvalidState());
|
||||
R_UNLESS(m_profile_importer->CanImportProfile(import.header.identifier_0), sprofile::ResultInvalidState());
|
||||
|
||||
/* Create temporary directories. */
|
||||
R_TRY(this->EnsureTemporaryDirectories());
|
||||
|
||||
/* Create profile. */
|
||||
char path[0x30];
|
||||
CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, data.identifier_0);
|
||||
R_TRY(WriteFile(path, std::addressof(data.data), sizeof(data.data)));
|
||||
CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, import.header.identifier_0);
|
||||
R_TRY(WriteFile(path, std::addressof(import.data), sizeof(import.data)));
|
||||
|
||||
/* Set profile imported. */
|
||||
m_profile_importer->OnImportProfile(data.identifier_0);
|
||||
m_profile_importer->OnImportProfile(import.header.identifier_0);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -231,7 +255,7 @@ namespace ams::sprofile::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
|
||||
Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
|
||||
/* Acquire locks. */
|
||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
@@ -240,8 +264,23 @@ namespace ams::sprofile::srv {
|
||||
R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
|
||||
R_UNLESS(m_profile_importer->CanImportMetadata(), sprofile::ResultInvalidState());
|
||||
|
||||
/* Check that the metadata we're importing is valid. */
|
||||
R_UNLESS(data.metadata.version == ProfileMetadataVersion, sprofile::ResultInvalidMetadataVersion());
|
||||
/* Check that the metadata we're importing is a valid version. */
|
||||
R_UNLESS(IsValidProfileFormatVersion(import.header.version), sprofile::ResultInvalidMetadataVersion());
|
||||
|
||||
/* Check that the metadata we're importing has a valid hash. */
|
||||
{
|
||||
crypto::Md5Generator md5;
|
||||
md5.Initialize();
|
||||
|
||||
md5.Update(std::addressof(import.header), sizeof(import.header));
|
||||
md5.Update(std::addressof(import.metadata), sizeof(import.metadata));
|
||||
md5.Update(std::addressof(import.profile_urls), sizeof(import.profile_urls[0]) * std::min<size_t>(import.metadata.num_entries, util::size(import.metadata.entries)));
|
||||
|
||||
u8 hash[crypto::Md5Generator::HashSize];
|
||||
md5.GetHash(hash, sizeof(hash));
|
||||
|
||||
R_UNLESS(crypto::IsSameBytes(hash, import.hash, sizeof(hash)), sprofile::ResultInvalidMetadataHash());
|
||||
}
|
||||
|
||||
/* Create temporary directories. */
|
||||
R_TRY(this->EnsureTemporaryDirectories());
|
||||
@@ -249,10 +288,10 @@ namespace ams::sprofile::srv {
|
||||
/* Create metadata. */
|
||||
char path[0x30];
|
||||
CreateTemporaryMetadataPath(path, sizeof(path), m_save_data_info.mount_name);
|
||||
R_TRY(WriteFile(path, std::addressof(data.metadata), sizeof(data.metadata)));
|
||||
R_TRY(WriteFile(path, std::addressof(import.metadata), sizeof(import.metadata)));
|
||||
|
||||
/* Import the metadata. */
|
||||
m_profile_importer->ImportMetadata(data.metadata);
|
||||
m_profile_importer->ImportMetadata(import.metadata);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -309,13 +348,13 @@ namespace ams::sprofile::srv {
|
||||
std::scoped_lock lk2(m_general_mutex);
|
||||
|
||||
/* Load the desired profile. */
|
||||
R_TRY(this->LoadProfile(profile));
|
||||
|
||||
/* Find the specified key. */
|
||||
for (auto i = 0u; i < m_service_profile->data.num_entries; ++i) {
|
||||
if (m_service_profile->data.entries[i].key == key) {
|
||||
*out = m_service_profile->data.entries[i];
|
||||
return ResultSuccess();
|
||||
if (R_SUCCEEDED(this->LoadProfile(profile))) {
|
||||
/* Find the specified key. */
|
||||
for (auto i = 0u; i < std::min<size_t>(m_service_profile->data.num_entries, util::size(m_service_profile->data.entries)); ++i) {
|
||||
if (m_service_profile->data.entries[i].key == key) {
|
||||
*out = m_service_profile->data.entries[i];
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +465,8 @@ namespace ams::sprofile::srv {
|
||||
}
|
||||
|
||||
void ProfileManager::OnCommitted() {
|
||||
/* TODO: Here, Nintendo sets the erpt ServiceProfileRevisionKey to the current revision key. */
|
||||
|
||||
/* If we need to, invalidate the loaded service profile. */
|
||||
if (m_service_profile.has_value()) {
|
||||
for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
|
||||
@@ -436,8 +477,6 @@ namespace ams::sprofile::srv {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Here, Nintendo sets the erpt ServiceProfileRevisionKey to the current revision key. */
|
||||
|
||||
/* Reset profile metadata. */
|
||||
m_profile_metadata = util::nullopt;
|
||||
|
||||
@@ -445,6 +484,9 @@ namespace ams::sprofile::srv {
|
||||
for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
|
||||
m_update_observer_manager.OnUpdate(m_profile_importer->GetImportingProfile(i));
|
||||
}
|
||||
|
||||
/* Reset profile importer. */
|
||||
m_profile_importer = util::nullopt;
|
||||
}
|
||||
|
||||
Result ProfileManager::EnsurePrimaryDirectories() {
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace ams::sprofile::srv {
|
||||
Result ResetSaveData();
|
||||
|
||||
Result OpenProfileImporter();
|
||||
void CloseProfileImporter();
|
||||
|
||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
||||
Result Commit();
|
||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ServiceForBgAgent::ReadMetadata(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg) {
|
||||
Result ServiceForBgAgent::GetImportableProfileUrls(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg) {
|
||||
/* Check size. */
|
||||
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
|
||||
|
||||
@@ -42,25 +42,31 @@ namespace ams::sprofile::srv {
|
||||
sprofile::srv::ProfileMetadata primary_metadata;
|
||||
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
|
||||
R_CATCH(fs::ResultPathNotFound) {
|
||||
/* If we have no metadata, we can't get any entries. */
|
||||
*out_count = 0;
|
||||
return ResultSuccess();
|
||||
/* It's okay if we have no primary metadata -- this means that all profiles are importable. */
|
||||
primary_metadata.num_entries = 0;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Copy matching entries. */
|
||||
/* We want to return the set of profiles that can be imported, which is just the profiles we don't already have. */
|
||||
u32 count = 0;
|
||||
for (u32 i = 0; i < arg.metadata.num_entries; ++i) {
|
||||
const auto &arg_entry = arg.metadata.entries[i];
|
||||
|
||||
/* Check if we have the entry. */
|
||||
bool have_entry = false;
|
||||
for (u32 j = 0; j < primary_metadata.num_entries; ++j) {
|
||||
const auto &pri_entry = primary_metadata.entries[j];
|
||||
|
||||
if (pri_entry.identifier_0 == arg_entry.identifier_0 && pri_entry.identifier_1 == arg_entry.identifier_1) {
|
||||
out[count++] = arg.entries[i];
|
||||
have_entry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't already have the entry, it's importable -- copy it out. */
|
||||
if (!have_entry) {
|
||||
out[count++] = arg.profile_urls[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Set output count. */
|
||||
@@ -68,6 +74,7 @@ namespace ams::sprofile::srv {
|
||||
return ResultSuccess();
|
||||
|
||||
}
|
||||
|
||||
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
|
||||
/* Load primary metadata. */
|
||||
bool loaded_metadata = true;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ams::sprofile::srv {
|
||||
constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
|
||||
public:
|
||||
Result OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out);
|
||||
Result ReadMetadata(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ReadMetadataEntry> &out, const sprofile::srv::ReadMetadataArgument &arg);
|
||||
Result GetImportableProfileUrls(sf::Out<u32> out_count, const sf::OutArray<sprofile::srv::ProfileUrl> &out, const sprofile::srv::ProfileMetadataForImportMetadata &arg);
|
||||
Result IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key);
|
||||
Result Reset();
|
||||
};
|
||||
|
||||
@@ -18,34 +18,11 @@
|
||||
|
||||
namespace ams::sprofile::srv {
|
||||
|
||||
struct ProfileMetadataEntry {
|
||||
Identifier identifier_0;
|
||||
Identifier identifier_1;
|
||||
u8 unk_0E[0x32];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileMetadataEntry>::value);
|
||||
static_assert(sizeof(ProfileMetadataEntry) == 0x40);
|
||||
constexpr inline const u32 ProfileFormatVersion = 1;
|
||||
|
||||
constexpr inline const u32 ProfileMetadataVersion = 0;
|
||||
|
||||
struct ProfileMetadata {
|
||||
u32 version;
|
||||
u32 num_entries;
|
||||
Identifier revision_key;
|
||||
u8 unk_10[0x30];
|
||||
ProfileMetadataEntry entries[50];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileMetadata>::value);
|
||||
static_assert(sizeof(ProfileMetadata) == 0xCC0);
|
||||
|
||||
struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||
ProfileMetadata metadata;
|
||||
u8 unk[0x8000 - sizeof(metadata)];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
|
||||
static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
|
||||
|
||||
constexpr inline const u32 ProfileDataVersion = 0;
|
||||
constexpr inline bool IsValidProfileFormatVersion(u32 version) {
|
||||
return version == ProfileFormatVersion;
|
||||
}
|
||||
|
||||
enum ValueType : u8 {
|
||||
ValueType_Byte = 0,
|
||||
@@ -69,15 +46,24 @@ namespace ams::sprofile::srv {
|
||||
static_assert(util::is_pod<ProfileDataEntry>::value);
|
||||
static_assert(sizeof(ProfileDataEntry) == 0x10);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ProfileDataEntry, key) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataEntry, type) == 0x07);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataEntry, value_s64) == 0x08);
|
||||
|
||||
struct ProfileData {
|
||||
u32 version;
|
||||
u32 num_entries;
|
||||
u8 unk_08[0x28];
|
||||
u8 unk_04[0x0C];
|
||||
u8 unk_10[0x20];
|
||||
ProfileDataEntry entries[(0x4000 - 0x30) / sizeof(ProfileDataEntry)];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileData>::value);
|
||||
static_assert(sizeof(ProfileData) == 0x4000);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ProfileData, num_entries) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileData, unk_04) == 0x04);
|
||||
static_assert(AMS_OFFSETOF(ProfileData, unk_10) == 0x10);
|
||||
static_assert(AMS_OFFSETOF(ProfileData, entries) == 0x30);
|
||||
|
||||
struct ServiceProfile {
|
||||
Identifier name;
|
||||
ProfileData data;
|
||||
@@ -85,29 +71,82 @@ namespace ams::sprofile::srv {
|
||||
static_assert(util::is_pod<ServiceProfile>::value);
|
||||
static_assert(sizeof(ServiceProfile) == 0x4008);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ServiceProfile, name) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ServiceProfile, data) == 0x08);
|
||||
|
||||
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||
Identifier identifier_0;
|
||||
Identifier identifier_1;
|
||||
u8 unk_0E[2];
|
||||
struct {
|
||||
Identifier identifier_0;
|
||||
Identifier identifier_1;
|
||||
u8 unk_0E[2];
|
||||
u32 version;
|
||||
u8 unk_14[0x1C];
|
||||
} header;
|
||||
u8 hash[crypto::Md5Generator::HashSize];
|
||||
ProfileData data;
|
||||
u8 unk_4010[0x4400 - 0x4010];
|
||||
u8 unk_4040[0x4400 - 0x4040];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileDataForImportData>::value);
|
||||
static_assert(sizeof(ProfileDataForImportData) == 0x4400);
|
||||
|
||||
struct ReadMetadataEntry {
|
||||
u8 unk[0x100];
|
||||
};
|
||||
static_assert(util::is_pod<ReadMetadataEntry>::value);
|
||||
static_assert(sizeof(ReadMetadataEntry) == 0x100);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataForImportData, header) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataForImportData, hash) == 0x30);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataForImportData, data) == 0x40);
|
||||
static_assert(AMS_OFFSETOF(ProfileDataForImportData, unk_4040) == 0x4040);
|
||||
|
||||
struct ReadMetadataArgument : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||
struct ProfileMetadataEntry {
|
||||
Identifier identifier_0;
|
||||
Identifier identifier_1;
|
||||
u8 unk_0E[0x32];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileMetadataEntry>::value);
|
||||
static_assert(sizeof(ProfileMetadataEntry) == 0x40);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, identifier_0) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, identifier_1) == 0x07);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataEntry, unk_0E) == 0x0E);
|
||||
|
||||
struct ProfileUrl : public sf::PrefersMapAliasTransferMode {
|
||||
char url[0x100];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileUrl>::value);
|
||||
static_assert(sizeof(ProfileUrl) == 0x100);
|
||||
|
||||
struct ProfileMetadata {
|
||||
u32 num_entries;
|
||||
u32 unk_04;
|
||||
Identifier revision_key;
|
||||
u8 unk_0F[0x1];
|
||||
u8 unk_10[0x30];
|
||||
ProfileMetadataEntry entries[50];
|
||||
};
|
||||
static_assert(util::is_pod<ProfileMetadata>::value);
|
||||
static_assert(sizeof(ProfileMetadata) == 0xCC0);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, num_entries) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_04) == 0x04);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, revision_key) == 0x08);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_0F) == 0x0F);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, unk_10) == 0x10);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadata, entries) == 0x40);
|
||||
|
||||
struct ProfileMetadataForImportMetadata : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||
struct {
|
||||
u32 version;
|
||||
u8 unk_04[0x1C];
|
||||
} header;
|
||||
u8 hash[crypto::Md5Generator::HashSize];
|
||||
ProfileMetadata metadata;
|
||||
ReadMetadataEntry entries[(0x8000 - sizeof(metadata)) / sizeof(ReadMetadataEntry)];
|
||||
u8 unk_7FC0[0x40];
|
||||
ProfileUrl profile_urls[50];
|
||||
u8 unk_3EF0[0x8000 - 0x3EF0];
|
||||
};
|
||||
static_assert(util::is_pod<ReadMetadataArgument>::value);
|
||||
static_assert(sizeof(ReadMetadataArgument) == 0x8000);
|
||||
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
|
||||
static_assert(sizeof(ProfileMetadataForImportMetadata) == 0x8000);
|
||||
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, header) == 0x00);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, hash) == 0x20);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, metadata) == 0x30);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, profile_urls) == 0xCF0);
|
||||
static_assert(AMS_OFFSETOF(ProfileMetadataForImportMetadata, unk_3EF0) == 0x3EF0);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 13
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||
|
||||
@@ -64,8 +64,9 @@
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_12_0_3 ATMOSPHERE_TARGET_FIRMWARE(12, 0, 3)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_12_1_0 ATMOSPHERE_TARGET_FIRMWARE(12, 1, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_0_0 ATMOSPHERE_TARGET_FIRMWARE(13, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_13_1_0 ATMOSPHERE_TARGET_FIRMWARE(13, 1, 0)
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_0_0
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_13_1_0
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||
@@ -122,6 +123,7 @@ namespace ams {
|
||||
TargetFirmware_12_0_3 = ATMOSPHERE_TARGET_FIRMWARE_12_0_3,
|
||||
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
||||
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_0,
|
||||
TargetFirmware_13_1_0 = ATMOSPHERE_TARGET_FIRMWARE_13_1_0,
|
||||
|
||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <vapours/crypto/crypto_memory_compare.hpp>
|
||||
#include <vapours/crypto/crypto_memory_clear.hpp>
|
||||
#include <vapours/crypto/crypto_md5_generator.hpp>
|
||||
#include <vapours/crypto/crypto_sha1_generator.hpp>
|
||||
#include <vapours/crypto/crypto_sha256_generator.hpp>
|
||||
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/crypto/impl/crypto_md5_impl.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
class Md5Generator {
|
||||
NON_COPYABLE(Md5Generator);
|
||||
NON_MOVEABLE(Md5Generator);
|
||||
private:
|
||||
using Impl = impl::Md5Impl;
|
||||
public:
|
||||
static constexpr size_t HashSize = Impl::HashSize;
|
||||
static constexpr size_t BlockSize = Impl::BlockSize;
|
||||
|
||||
static constexpr inline const u8 Asn1Identifier[] = {
|
||||
0x30, 0x20, /* Sequence, size 0x20 */
|
||||
0x30, 0x0C, /* Sequence, size 0x0C */
|
||||
0x06, 0x08, /* Object Identifier */
|
||||
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* MD5 */
|
||||
0x05, 0x00, /* Null */
|
||||
0x04, 0x10, /* Octet string, size 0x10 */
|
||||
};
|
||||
static constexpr size_t Asn1IdentifierSize = util::size(Asn1Identifier);
|
||||
private:
|
||||
Impl m_impl;
|
||||
public:
|
||||
Md5Generator() { /* ... */ }
|
||||
|
||||
void Initialize() {
|
||||
m_impl.Initialize();
|
||||
}
|
||||
|
||||
void Update(const void *data, size_t size) {
|
||||
m_impl.Update(data, size);
|
||||
}
|
||||
|
||||
void GetHash(void *dst, size_t size) {
|
||||
m_impl.GetHash(dst, size);
|
||||
}
|
||||
};
|
||||
|
||||
void GenerateMd5Hash(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 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/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/crypto/impl/crypto_hash_function.hpp>
|
||||
#include <vapours/crypto/crypto_memory_clear.hpp>
|
||||
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
class Md5Impl {
|
||||
public:
|
||||
static constexpr size_t HashSize = 0x10;
|
||||
static constexpr size_t BlockSize = 0x40;
|
||||
private:
|
||||
enum State {
|
||||
State_None = 0,
|
||||
State_Initialized = 1,
|
||||
State_Done = 2,
|
||||
};
|
||||
private:
|
||||
union {
|
||||
struct {
|
||||
u32 a, b, c, d;
|
||||
} p;
|
||||
u32 state[4];
|
||||
} m_x;
|
||||
alignas(8) u8 m_y[BlockSize];
|
||||
size_t m_size;
|
||||
State m_state;
|
||||
public:
|
||||
Md5Impl() : m_state(State_None) { /* ... */ }
|
||||
~Md5Impl() { ClearMemory(this, sizeof(*this)); }
|
||||
|
||||
void Initialize();
|
||||
void Update(const void *data, size_t size);
|
||||
void GetHash(void *dst, size_t size);
|
||||
private:
|
||||
void ProcessBlock();
|
||||
void ProcessLastBlock();
|
||||
};
|
||||
|
||||
/* static_assert(HashFunction<Md5Impl>); */
|
||||
|
||||
}
|
||||
@@ -37,6 +37,8 @@ namespace ams::sprofile {
|
||||
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
|
||||
R_DEFINE_ERROR_RESULT(InvalidMetadataHash, 3211);
|
||||
R_DEFINE_ERROR_RESULT(InvalidDataVersion, 3230);
|
||||
R_DEFINE_ERROR_RESULT(InvalidDataHash, 3231);
|
||||
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace ams::svc {
|
||||
}
|
||||
}
|
||||
|
||||
return util::ScaleByConstantFactor<s64, TicksPerSecond, NanoSecondsPerSecond>(ns);
|
||||
return util::ScaleByConstantFactorUp<s64, TicksPerSecond, NanoSecondsPerSecond>(ns);
|
||||
}
|
||||
public:
|
||||
constexpr ALWAYS_INLINE explicit Tick(s64 t = 0) : m_tick(t) { /* ... */ }
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace ams::svc {
|
||||
/* Thread types. */
|
||||
using ThreadFunc = ams::svc::Address;
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
#if defined(ATMOSPHERE_ARCH_ARM_V8A)
|
||||
|
||||
struct ThreadContext {
|
||||
u64 r[29];
|
||||
@@ -268,7 +268,7 @@ namespace ams::svc {
|
||||
};
|
||||
static_assert(sizeof(ThreadContext) == 0x320);
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM_V7A)
|
||||
|
||||
struct ThreadContext {
|
||||
u32 r[13];
|
||||
@@ -282,9 +282,14 @@ namespace ams::svc {
|
||||
u32 fpexc;
|
||||
u32 tpidr;
|
||||
};
|
||||
static_assert(sizeof(ThreadContext) == 0x158);
|
||||
|
||||
#else
|
||||
|
||||
#if !defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#error "Unknown Architecture for ams::svc::ThreadContext"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
enum ThreadSuspend : u32 {
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ams::svc {
|
||||
/* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */
|
||||
/* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */
|
||||
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(13);
|
||||
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 3);
|
||||
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 4);
|
||||
|
||||
constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace ams::util {
|
||||
}
|
||||
|
||||
template<typename T, T N, T D>
|
||||
constexpr ALWAYS_INLINE T ScaleByConstantFactor(const T V) {
|
||||
constexpr ALWAYS_INLINE T ScaleByConstantFactorUp(const T V) {
|
||||
/* Multiplying and dividing by large numerator/denominator can cause error to be introduced. */
|
||||
/* This algorithm multiples/divides in stages, so as to mitigate this (particularly with large denominator). */
|
||||
|
||||
@@ -279,4 +279,20 @@ namespace ams::util {
|
||||
return (D * Quot_N * Quot_V) + (Quot_V * Rem_N) + (Rem_V * Quot_N) + rem_mult;
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T RotateLeft(T v, int n) {
|
||||
using Unsigned = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(Unsigned) == sizeof(T));
|
||||
|
||||
return static_cast<T>(std::rotl<Unsigned>(static_cast<Unsigned>(v), n));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
constexpr ALWAYS_INLINE T RotateRight(T v, int n) {
|
||||
using Unsigned = typename std::make_unsigned<T>::type;
|
||||
static_assert(sizeof(Unsigned) == sizeof(T));
|
||||
|
||||
return static_cast<T>(std::rotr<Unsigned>(static_cast<Unsigned>(v), n));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
constexpr bool IsLittleEndian() {
|
||||
consteval bool IsLittleEndian() {
|
||||
return std::endian::native == std::endian::little;
|
||||
}
|
||||
|
||||
constexpr bool IsBigEndian() {
|
||||
consteval bool IsBigEndian() {
|
||||
return std::endian::native == std::endian::big;
|
||||
}
|
||||
|
||||
|
||||
28
libraries/libvapours/source/crypto/crypto_md5_generator.cpp
Normal file
28
libraries/libvapours/source/crypto/crypto_md5_generator.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 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 <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateMd5Hash(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
Md5Generator gen;
|
||||
|
||||
gen.Initialize();
|
||||
gen.Update(src, src_size);
|
||||
gen.GetHash(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
255
libraries/libvapours/source/crypto/impl/crypto_md5_impl.cpp
Normal file
255
libraries/libvapours/source/crypto/impl/crypto_md5_impl.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 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 <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Md5Constants {
|
||||
static constexpr const u32 A = 0x67452301;
|
||||
static constexpr const u32 B = 0xEFCDAB89;
|
||||
static constexpr const u32 C = 0x98BADCFE;
|
||||
static constexpr const u32 D = 0x10325476;
|
||||
|
||||
static constexpr const u32 T[] = {
|
||||
0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
|
||||
0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
|
||||
0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
|
||||
0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
|
||||
0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
|
||||
0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
|
||||
0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
|
||||
0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
|
||||
0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
|
||||
0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
|
||||
0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
|
||||
0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
|
||||
0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
|
||||
0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
|
||||
0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
|
||||
0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391,
|
||||
};
|
||||
|
||||
static constexpr u32 K[] = {
|
||||
0x1, 0x6, 0xB, 0x0,
|
||||
0x5, 0xA, 0xF, 0x4,
|
||||
0x9, 0xE, 0x3, 0x8,
|
||||
0xD, 0x2, 0x7, 0xC,
|
||||
0x5, 0x8, 0xB, 0xE,
|
||||
0x1, 0x4, 0x7, 0xA,
|
||||
0xD, 0x0, 0x3, 0x6,
|
||||
0x9, 0xC, 0xF, 0x2,
|
||||
0x0, 0x7, 0xE, 0x5,
|
||||
0xC, 0x3, 0xA, 0x1,
|
||||
0x8, 0xF, 0x6, 0xD,
|
||||
0x4, 0xB, 0x2, 0x9,
|
||||
};
|
||||
|
||||
static constexpr u8 Padding[] = {
|
||||
0x80
|
||||
};
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); }
|
||||
constexpr ALWAYS_INLINE u32 G(u32 x, u32 y, u32 z) { return (x & z) | (y & (~z)); }
|
||||
constexpr ALWAYS_INLINE u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; }
|
||||
constexpr ALWAYS_INLINE u32 I(u32 x, u32 y, u32 z) { return y ^ (x | (~z)); }
|
||||
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound1(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + F(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound2(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + G(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound3(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + H(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound4(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + I(b, c, d) + x + t, s); }
|
||||
|
||||
void Encode(u32 *dst, const u32 *src, size_t size) {
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < size; i += sizeof(u32)) {
|
||||
util::StoreLittleEndian(dst + i, src[i]);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
|
||||
void Decode(u32 *dst, const u32 *src, size_t size) {
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < size; i += sizeof(u32)) {
|
||||
dst[i] = util::LoadLittleEndian(src + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Md5Impl::Initialize() {
|
||||
/* Set constants. */
|
||||
m_x.p.a = Md5Constants::A;
|
||||
m_x.p.b = Md5Constants::B;
|
||||
m_x.p.c = Md5Constants::C;
|
||||
m_x.p.d = Md5Constants::D;
|
||||
|
||||
/* Set size. */
|
||||
m_size = 0;
|
||||
|
||||
/* Set initialized. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Md5Impl::Update(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Determine how much we can process. */
|
||||
const size_t work_idx = m_size % BlockSize;
|
||||
const size_t work_remaining = BlockSize - work_idx;
|
||||
|
||||
/* Increment our size. */
|
||||
m_size += size;
|
||||
|
||||
/* Copy in the data to our buffer, if we don't have a full block. */
|
||||
if (work_remaining > size) {
|
||||
if (size > 0) {
|
||||
std::memcpy(m_y + work_idx, data, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy what we can to complete our block. */
|
||||
std::memcpy(m_y + work_idx, data, work_remaining);
|
||||
|
||||
/* Process the block. */
|
||||
this->ProcessBlock();
|
||||
|
||||
/* Adjust size to account for what we've processed. */
|
||||
size -= work_remaining;
|
||||
|
||||
/* Process as many full blocks as we can. */
|
||||
const u8 *cur_block = static_cast<const u8 *>(data) + work_remaining;
|
||||
for (size_t i = 0; i < size / BlockSize; ++i) {
|
||||
std::memcpy(m_y, cur_block, BlockSize);
|
||||
cur_block += BlockSize;
|
||||
|
||||
this->ProcessBlock();
|
||||
}
|
||||
|
||||
/* Copy in any leftover data. */
|
||||
if (const auto left = size % BlockSize; left > 0) {
|
||||
std::memcpy(m_y, cur_block, left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Md5Impl::GetHash(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, finish processing. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Encode the result. */
|
||||
Encode(static_cast<u32 *>(dst), m_x.state, HashSize);
|
||||
}
|
||||
|
||||
void Md5Impl::ProcessBlock() {
|
||||
/* Declare tracking pointers for rounds. */
|
||||
u32 x[BlockSize / sizeof(u32)];
|
||||
const u32 *p_t = Md5Constants::T;
|
||||
const u32 *p_k = Md5Constants::K;
|
||||
const u32 *p_x = x;
|
||||
|
||||
/* Extract current state. */
|
||||
u32 a = m_x.p.a;
|
||||
u32 b = m_x.p.b;
|
||||
u32 c = m_x.p.c;
|
||||
u32 d = m_x.p.d;
|
||||
|
||||
/* Decode the block into native endian. */
|
||||
Decode(x, reinterpret_cast<const u32 *>(m_y), BlockSize);
|
||||
|
||||
/* Perform round 1. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound1(a, b, c, d, *p_x++, 7, *p_t++);
|
||||
d = CalculateRound1(d, a, b, c, *p_x++, 12, *p_t++);
|
||||
c = CalculateRound1(c, d, a, b, *p_x++, 17, *p_t++);
|
||||
b = CalculateRound1(b, c, d, a, *p_x++, 22, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 2. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound2(a, b, c, d, x[*p_k++], 5, *p_t++);
|
||||
d = CalculateRound2(d, a, b, c, x[*p_k++], 9, *p_t++);
|
||||
c = CalculateRound2(c, d, a, b, x[*p_k++], 14, *p_t++);
|
||||
b = CalculateRound2(b, c, d, a, x[*p_k++], 20, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 3. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound3(a, b, c, d, x[*p_k++], 4, *p_t++);
|
||||
d = CalculateRound3(d, a, b, c, x[*p_k++], 11, *p_t++);
|
||||
c = CalculateRound3(c, d, a, b, x[*p_k++], 16, *p_t++);
|
||||
b = CalculateRound3(b, c, d, a, x[*p_k++], 23, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 4. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound4(a, b, c, d, x[*p_k++], 6, *p_t++);
|
||||
d = CalculateRound4(d, a, b, c, x[*p_k++], 10, *p_t++);
|
||||
c = CalculateRound4(c, d, a, b, x[*p_k++], 15, *p_t++);
|
||||
b = CalculateRound4(b, c, d, a, x[*p_k++], 21, *p_t++);
|
||||
}
|
||||
|
||||
/* Mix the result back into our state. */
|
||||
m_x.p.a += a;
|
||||
m_x.p.b += b;
|
||||
m_x.p.c += c;
|
||||
m_x.p.d += d;
|
||||
}
|
||||
|
||||
void Md5Impl::ProcessLastBlock() {
|
||||
/* Get bit count. */
|
||||
const u64 bit_count = m_size * BITSIZEOF(u8);
|
||||
|
||||
/* Add padding byte unconditionally. */
|
||||
this->Update(Md5Constants::Padding, sizeof(Md5Constants::Padding));
|
||||
|
||||
/* Determine remaining. */
|
||||
size_t work_idx = m_size % BlockSize;
|
||||
size_t work_remaining = BlockSize - work_idx;
|
||||
|
||||
/* We want to process 8000.....{bit count}. */
|
||||
if (work_remaining < sizeof(u64)) {
|
||||
std::memset(m_y + work_idx, 0, work_remaining);
|
||||
this->ProcessBlock();
|
||||
work_idx = 0;
|
||||
work_remaining = BlockSize;
|
||||
}
|
||||
if (work_remaining > sizeof(u64)) {
|
||||
std::memset(m_y + work_idx, 0, work_remaining - sizeof(u64));
|
||||
}
|
||||
|
||||
util::StoreLittleEndian<u64>(reinterpret_cast<u64 *>(m_y + BlockSize - sizeof(u64)), bit_count);
|
||||
|
||||
this->ProcessBlock();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,10 +49,9 @@ namespace ams::kern::init {
|
||||
constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
|
||||
|
||||
void StoreDataCache(const void *addr, size_t size) {
|
||||
uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
|
||||
uintptr_t end = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start; cur < end; cur += cpu::DataCacheLineSize) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(cur) : "memory");
|
||||
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
|
||||
for (size_t stored = 0; stored < size; stored += cpu::DataCacheLineSize) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(start + stored) : "memory");
|
||||
}
|
||||
cpu::DataSynchronizationBarrier();
|
||||
}
|
||||
@@ -594,11 +593,13 @@ namespace ams::kern::init {
|
||||
|
||||
switch (num_watchpoints) {
|
||||
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0)
|
||||
case 0:
|
||||
cpu::SetDbgWcr0El1(0);
|
||||
cpu::SetDbgWvr0El1(0);
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cpu::SetDbgWcr0El1(0);
|
||||
cpu::SetDbgWvr0El1(0);
|
||||
|
||||
switch (num_breakpoints) {
|
||||
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0)
|
||||
|
||||
@@ -376,7 +376,7 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv:
|
||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
mrs x20, sp_el0
|
||||
ldr x20, [sp]
|
||||
mrs x21, elr_el1
|
||||
mrs x22, spsr_el1
|
||||
mrs x23, tpidr_el0
|
||||
@@ -529,7 +529,7 @@ _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv:
|
||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
mrs x20, sp_el0
|
||||
ldr x20, [sp]
|
||||
mrs x21, elr_el1
|
||||
mrs x22, spsr_el1
|
||||
mrs x23, tpidr_el0
|
||||
|
||||
@@ -126,7 +126,8 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
||||
cmp x7, x18
|
||||
b.ne 1f
|
||||
|
||||
/* If they're the same, then we can just return as there's nothing to do. */
|
||||
/* If they're the same, then we can just issue a memory barrier and return. */
|
||||
dmb ish
|
||||
ret
|
||||
|
||||
0: /* The interrupt task thread is runnable. */
|
||||
@@ -182,9 +183,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
||||
|
||||
/* Get the highest priority thread's context, and save it. */
|
||||
/* ams::kern::KThread::GetContextForSchedulerLoop() */
|
||||
mov x0, x21
|
||||
bl _ZN3ams4kern7KThread26GetContextForSchedulerLoopEv
|
||||
mov x22, x0
|
||||
add x22, x21, #(THREAD_THREAD_CONTEXT)
|
||||
|
||||
/* Prepare to try to acquire the context lock. */
|
||||
add x1, x22, #(THREAD_CONTEXT_LOCKED)
|
||||
@@ -226,26 +225,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
||||
mov x0, x22
|
||||
RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f)
|
||||
|
||||
9: /* Configure single-step, if we should. */
|
||||
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
|
||||
|
||||
/* Get a reference to the new thread's stack parameters. */
|
||||
add x2, sp, #0x1000
|
||||
and x2, x2, #~(0x1000-1)
|
||||
|
||||
/* Read the single-step flag. */
|
||||
ldurb w2, [x2, #-(THREAD_STACK_PARAMETERS_SIZE - THREAD_STACK_PARAMETERS_IS_SINGLE_STEP)]
|
||||
|
||||
/* Update the single-step bit in mdscr_el1. */
|
||||
mrs x1, mdscr_el1
|
||||
bic x1, x1, #1
|
||||
orr x1, x1, x2
|
||||
msr mdscr_el1, x1
|
||||
|
||||
isb
|
||||
#endif
|
||||
|
||||
/* We're done restoring the thread context, and can return safely. */
|
||||
9: /* We're done restoring the thread context, and can return safely. */
|
||||
ret
|
||||
|
||||
10: /* Our switch failed. */
|
||||
|
||||
@@ -378,6 +378,12 @@ namespace ams::settings::fwdbg {
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0"));
|
||||
|
||||
/* Controls whether atmosphere's dmnt.gen2 gdbstub should run as a standalone via sockets. */
|
||||
/* Note that this setting is ignored (and treated as 0) when htc is enabled. */
|
||||
/* Note that this setting may disappear in the future. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_standalone_gdbstub", "u8!0x0"));
|
||||
|
||||
/* Controls whether atmosphere's log manager is enabled. */
|
||||
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
|
||||
@@ -101,11 +101,9 @@ namespace ams::creport {
|
||||
m_has_extra_info = has_extra_info;
|
||||
|
||||
if (this->OpenProcess(process_id)) {
|
||||
ON_SCOPE_EXIT { this->Close(); };
|
||||
|
||||
/* Parse info from the crashed process. */
|
||||
this->ProcessExceptions();
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread);
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_crashed_thread, this->Is64Bit());
|
||||
m_thread_list->ReadFromProcess(m_debug_handle, m_thread_tls_map, this->Is64Bit());
|
||||
|
||||
/* Associate module list to threads. */
|
||||
@@ -120,7 +118,7 @@ namespace ams::creport {
|
||||
/* Nintendo's creport finds extra modules by looking at all threads if application, */
|
||||
/* but there's no reason for us not to always go looking. */
|
||||
for (size_t i = 0; i < m_thread_list->GetThreadCount(); i++) {
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i));
|
||||
m_module_list->FindModulesFromThreadInfo(m_debug_handle, m_thread_list->GetThreadInfo(i), this->Is64Bit());
|
||||
}
|
||||
|
||||
/* Cache the module base address to send to fatal. */
|
||||
@@ -324,6 +322,11 @@ namespace ams::creport {
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're open, we need to close here. */
|
||||
if (this->IsOpen()) {
|
||||
this->Close();
|
||||
}
|
||||
|
||||
/* Finalize our heap. */
|
||||
std::destroy_at(m_module_list);
|
||||
std::destroy_at(m_thread_list);
|
||||
@@ -358,7 +361,7 @@ namespace ams::creport {
|
||||
}
|
||||
|
||||
void CrashReport::SaveToFile(ScopedFile &file) {
|
||||
file.WriteFormat("Atmosphère Crash Report (v1.6):\n");
|
||||
file.WriteFormat("Atmosphère Crash Report (v1.7):\n");
|
||||
|
||||
file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", m_result.GetValue(), m_result.GetModule(), m_result.GetDescription());
|
||||
|
||||
|
||||
@@ -103,13 +103,17 @@ namespace ams {
|
||||
g_crash_report.Initialize();
|
||||
|
||||
/* Try to debug the crashed process. */
|
||||
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
||||
if (!g_crash_report.IsComplete()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
g_crash_report.BuildReport(crashed_pid, has_extra_info);
|
||||
ON_SCOPE_EXIT { if (g_crash_report.IsOpen()) { g_crash_report.Close(); } };
|
||||
|
||||
/* Save report to file. */
|
||||
g_crash_report.SaveReport(enable_screenshot);
|
||||
if (!g_crash_report.IsComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save report to file. */
|
||||
g_crash_report.SaveReport(enable_screenshot);
|
||||
}
|
||||
|
||||
/* Try to terminate the process, if we should. */
|
||||
const auto fw_ver = hos::GetVersion();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ams::creport {
|
||||
|
||||
struct ModulePath {
|
||||
u32 zero;
|
||||
u32 path_length;
|
||||
s32 path_length;
|
||||
char path[ModulePathLengthMax];
|
||||
};
|
||||
static_assert(sizeof(ModulePath) == 0x208, "ModulePath definition!");
|
||||
@@ -58,26 +58,26 @@ namespace ams::creport {
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread) {
|
||||
void ModuleList::FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit) {
|
||||
/* Set the debug handle, for access in other member functions. */
|
||||
m_debug_handle = debug_handle;
|
||||
|
||||
/* Try to add the thread's PC. */
|
||||
this->TryAddModule(thread.GetPC());
|
||||
this->TryAddModule(thread.GetPC(), is_64_bit);
|
||||
|
||||
/* Try to add the thread's LR. */
|
||||
this->TryAddModule(thread.GetLR());
|
||||
this->TryAddModule(thread.GetLR(), is_64_bit);
|
||||
|
||||
/* Try to add all the addresses in the thread's stacktrace. */
|
||||
for (size_t i = 0; i < thread.GetStackTraceSize(); i++) {
|
||||
this->TryAddModule(thread.GetStackTrace(i));
|
||||
this->TryAddModule(thread.GetStackTrace(i), is_64_bit);
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleList::TryAddModule(uintptr_t guess) {
|
||||
void ModuleList::TryAddModule(uintptr_t guess, bool is_64_bit) {
|
||||
/* Try to locate module from guess. */
|
||||
uintptr_t base_address = 0;
|
||||
if (!this->TryFindModule(std::addressof(base_address), guess)) {
|
||||
if (!this->TryFindModule(std::addressof(base_address), guess, is_64_bit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,9 +105,18 @@ namespace ams::creport {
|
||||
module.end_address = mi.base_address + mi.size;
|
||||
GetModuleName(module.name, module.start_address, module.end_address);
|
||||
GetModuleId(module.module_id, module.end_address);
|
||||
/* Some homebrew won't have a name. Add a fake one for readability. */
|
||||
|
||||
/* Default to no symbol table. */
|
||||
module.has_sym_table = false;
|
||||
|
||||
if (std::strcmp(module.name, "") == 0) {
|
||||
/* Some homebrew won't have a name. Add a fake one for readability. */
|
||||
util::SNPrintf(module.name, sizeof(module.name), "[%02x%02x%02x%02x]", module.module_id[0], module.module_id[1], module.module_id[2], module.module_id[3]);
|
||||
} else {
|
||||
/* The module has a name, and so might have a symbol table. Try to add it, if it does. */
|
||||
if (is_64_bit) {
|
||||
DetectModuleSymbolTable(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +134,9 @@ namespace ams::creport {
|
||||
}
|
||||
}
|
||||
|
||||
bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess) {
|
||||
bool ModuleList::TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit) {
|
||||
AMS_UNUSED(is_64_bit);
|
||||
|
||||
/* Query the memory region our guess falls in. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
@@ -199,7 +210,7 @@ namespace ams::creport {
|
||||
}
|
||||
|
||||
/* Also validate that we're looking at a valid name. */
|
||||
if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length != strnlen(rodata_start.module_path.path, sizeof(rodata_start.module_path.path))) {
|
||||
if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +219,7 @@ namespace ams::creport {
|
||||
/* Start after last slash in path. */
|
||||
const char *path = rodata_start.module_path.path;
|
||||
int ofs;
|
||||
for (ofs = rodata_start.module_path.path_length; ofs >= 0; ofs--) {
|
||||
for (ofs = std::min<size_t>(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)); ofs >= 0; ofs--) {
|
||||
if (path[ofs] == '/' || path[ofs] == '\\') {
|
||||
break;
|
||||
}
|
||||
@@ -216,8 +227,8 @@ namespace ams::creport {
|
||||
ofs++;
|
||||
|
||||
/* Copy name to output. */
|
||||
const size_t name_size = std::min(ModuleNameLengthMax, sizeof(rodata_start.module_path.path) - ofs);
|
||||
std::strncpy(out_name, path + ofs, name_size);
|
||||
const size_t name_size = std::min(ModuleNameLengthMax, std::min<size_t>(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)) - ofs);
|
||||
std::memcpy(out_name, path + ofs, name_size);
|
||||
out_name[ModuleNameLengthMax - 1] = '\x00';
|
||||
}
|
||||
|
||||
@@ -247,6 +258,133 @@ namespace ams::creport {
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleList::DetectModuleSymbolTable(ModuleInfo &module) {
|
||||
/* If we already have a symbol table, no more parsing is needed. */
|
||||
if (module.has_sym_table) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Declare temporaries. */
|
||||
u64 temp_64;
|
||||
u32 temp_32;
|
||||
|
||||
/* Get module state. */
|
||||
svc::MemoryInfo mi;
|
||||
svc::PageInfo pi;
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.start_address))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto module_state = mi.state;
|
||||
|
||||
/* Verify .rodata is read-only with same state as .text. */
|
||||
if (R_FAILED(svc::QueryDebugProcessMemory(std::addressof(mi), std::addressof(pi), m_debug_handle, module.end_address)) || mi.permission != svc::MemoryPermission_Read || mi.state != module_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the first instruction of .text. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address, sizeof(temp_32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We want to find the symbol table/.dynamic. */
|
||||
uintptr_t dyn_address = 0;
|
||||
uintptr_t sym_tab = 0;
|
||||
uintptr_t str_tab = 0;
|
||||
size_t num_sym = 0;
|
||||
|
||||
/* Detect module type. */
|
||||
if (temp_32 == 0) {
|
||||
/* Module is dynamically loaded by rtld. */
|
||||
u32 mod_offset;
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(mod_offset)), m_debug_handle, module.start_address + sizeof(u32), sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset, sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (temp_32 != rocrt::ModuleHeaderVersion) { /* MOD0 */
|
||||
return;
|
||||
}
|
||||
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + mod_offset + sizeof(u32), sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
dyn_address = module.start_address + mod_offset + temp_32;
|
||||
} else if (temp_32 == 0x14000002) {
|
||||
/* Module embeds rtld. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x5C, sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (temp_32 != 0x94000002) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + 0x60, sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
dyn_address = module.start_address + 0x60 + temp_32;
|
||||
} else {
|
||||
/* Module has unknown format. */
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Locate tables inside .dyn. */
|
||||
for (size_t ofs = 0; /* ... */; ofs += 0x10) {
|
||||
/* Read the DynamicTag. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs, sizeof(u64)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (temp_64 == 0) {
|
||||
/* We're done parsing .dyn. */
|
||||
break;
|
||||
} else if (temp_64 == 4) {
|
||||
/* We found DT_HASH */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read nchain, to get the number of symbols. */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_32)), m_debug_handle, module.start_address + temp_64 + sizeof(u32), sizeof(u32)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
num_sym = temp_32;
|
||||
} else if (temp_64 == 5) {
|
||||
/* We found DT_STRTAB */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
str_tab = module.start_address + temp_64;
|
||||
} else if (temp_64 == 6) {
|
||||
/* We found DT_SYMTAB */
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(temp_64)), m_debug_handle, dyn_address + ofs + sizeof(u64), sizeof(u64)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
sym_tab = module.start_address + temp_64;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that we found all the tables. */
|
||||
if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
module.has_sym_table = true;
|
||||
module.sym_tab = sym_tab;
|
||||
module.str_tab = str_tab;
|
||||
module.num_sym = static_cast<u32>(num_sym);
|
||||
}
|
||||
|
||||
const char *ModuleList::GetFormattedAddressString(uintptr_t address) {
|
||||
/* Print default formatted string. */
|
||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx", address);
|
||||
@@ -255,8 +393,52 @@ namespace ams::creport {
|
||||
for (size_t i = 0; i < m_num_modules; i++) {
|
||||
const auto& module = m_modules[i];
|
||||
if (module.start_address <= address && address < module.end_address) {
|
||||
if (module.has_sym_table) {
|
||||
/* Try to locate an appropriate symbol. */
|
||||
for (size_t j = 0; j < module.num_sym; ++j) {
|
||||
/* Read symbol from the module's symbol table. */
|
||||
struct {
|
||||
u32 st_name;
|
||||
u8 st_info;
|
||||
u8 st_other;
|
||||
u16 st_shndx;
|
||||
u64 st_value;
|
||||
u64 st_size;
|
||||
} sym;
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(sym)), m_debug_handle, module.sym_tab + j * sizeof(sym), sizeof(sym)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check the symbol is valid/STT_FUNC. */
|
||||
if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) {
|
||||
continue;
|
||||
}
|
||||
if ((sym.st_info & 0xF) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the address. */
|
||||
const uintptr_t func_start = module.start_address + sym.st_value;
|
||||
if (func_start <= address && address < func_start + sym.st_size) {
|
||||
/* Read the symbol name. */
|
||||
const uintptr_t sym_address = module.str_tab + sym.st_name;
|
||||
char sym_name[0x80];
|
||||
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(sym_name), m_debug_handle, sym_address, sizeof(sym_name)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ensure null-termination. */
|
||||
sym_name[sizeof(sym_name) - 1] = '\x00';
|
||||
|
||||
/* Print the symbol. */
|
||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx) (%s + 0x%lx)", address, module.name, address - module.start_address, sym_name, address - func_start);
|
||||
return m_address_str_buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::SNPrintf(m_address_str_buf, sizeof(m_address_str_buf), "%016lx (%s + 0x%lx)", address, module.name, address - module.start_address);
|
||||
break;
|
||||
return m_address_str_buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace ams::creport {
|
||||
u8 module_id[ModuleIdSize];
|
||||
u64 start_address;
|
||||
u64 end_address;
|
||||
bool has_sym_table;
|
||||
u64 sym_tab;
|
||||
u64 str_tab;
|
||||
u32 num_sym;
|
||||
};
|
||||
private:
|
||||
os::NativeHandle m_debug_handle;
|
||||
@@ -37,7 +41,7 @@ namespace ams::creport {
|
||||
ModuleInfo m_modules[ModuleCountMax];
|
||||
|
||||
/* For pretty-printing. */
|
||||
char m_address_str_buf[0x280];
|
||||
char m_address_str_buf[1_KB];
|
||||
public:
|
||||
ModuleList() : m_debug_handle(os::InvalidNativeHandle), m_num_modules(0) {
|
||||
std::memset(m_modules, 0, sizeof(m_modules));
|
||||
@@ -51,14 +55,15 @@ namespace ams::creport {
|
||||
return m_modules[i].start_address;
|
||||
}
|
||||
|
||||
void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread);
|
||||
void FindModulesFromThreadInfo(os::NativeHandle debug_handle, const ThreadInfo &thread, bool is_64_bit);
|
||||
const char *GetFormattedAddressString(uintptr_t address);
|
||||
void SaveToFile(ScopedFile &file);
|
||||
private:
|
||||
bool TryFindModule(uintptr_t *out_address, uintptr_t guess);
|
||||
void TryAddModule(uintptr_t guess);
|
||||
bool TryFindModule(uintptr_t *out_address, uintptr_t guess, bool is_64_bit);
|
||||
void TryAddModule(uintptr_t guess, bool is_64_bit);
|
||||
void GetModuleName(char *out_name, uintptr_t text_start, uintptr_t ro_start);
|
||||
void GetModuleId(u8 *out, uintptr_t ro_start);
|
||||
void DetectModuleSymbolTable(ModuleInfo &module);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs"],
|
||||
"service_access": ["pm:dmnt", "ldr:dmnt", "ro:dmnt", "ns:dev", "lr", "fsp-srv", "fatal:u", "pgl", "htcs", "bsd:s", "set:sys"],
|
||||
"service_host": [],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "dmnt2_debug_log.hpp"
|
||||
|
||||
#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG
|
||||
// TODO: This should be converted to use log manager. */
|
||||
//#define AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG
|
||||
|
||||
#if defined(AMS_DMNT2_ENABLE_HTCS_DEBUG_LOG)
|
||||
|
||||
@@ -164,15 +165,78 @@ namespace ams::dmnt {
|
||||
|
||||
namespace ams::dmnt {
|
||||
|
||||
//#define AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG
|
||||
|
||||
#if defined(AMS_DMNT2_ENABLE_SD_CARD_DEBUG_LOG)
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(0x40) constinit u8 g_buffer[os::MemoryPageSize * 4];
|
||||
constinit lmem::HeapHandle g_debug_log_heap;
|
||||
constinit fs::FileHandle g_debug_log_file;
|
||||
|
||||
constinit os::SdkMutex g_fs_mutex;
|
||||
constinit s64 g_fs_offset = 0;
|
||||
|
||||
void *Allocate(size_t size) {
|
||||
return lmem::AllocateFromExpHeap(g_debug_log_heap, size);
|
||||
}
|
||||
|
||||
void Deallocate(void *p, size_t size) {
|
||||
AMS_UNUSED(size);
|
||||
return lmem::FreeToExpHeap(g_debug_log_heap, p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeDebugLog() {
|
||||
/* Do nothing. */
|
||||
g_debug_log_heap = lmem::CreateExpHeap(g_buffer, sizeof(g_buffer), lmem::CreateOption_ThreadSafe);
|
||||
|
||||
fs::SetAllocator(Allocate, Deallocate);
|
||||
fs::InitializeForSystem();
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
R_ABORT_UNLESS(fs::MountSdCard("sdmc"));
|
||||
|
||||
fs::DeleteFile("sdmc:/dmnt2.log");
|
||||
R_ABORT_UNLESS(fs::CreateFile("sdmc:/dmnt2.log", 0));
|
||||
R_ABORT_UNLESS(fs::OpenFile(std::addressof(g_debug_log_file), "sdmc:/dmnt2.log", fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
}
|
||||
|
||||
void DebugLog(const char *prefix, const char *fmt, ...) {
|
||||
/* Do nothing. */
|
||||
char buffer[0x200];
|
||||
{
|
||||
const auto prefix_len = std::strlen(prefix);
|
||||
std::memcpy(buffer, prefix, prefix_len);
|
||||
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
util::VSNPrintf(buffer + prefix_len, sizeof(buffer) - prefix_len, fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
const auto len = std::strlen(buffer);
|
||||
|
||||
std::scoped_lock lk(g_fs_mutex);
|
||||
R_ABORT_UNLESS(fs::WriteFile(g_debug_log_file, g_fs_offset, buffer, len, fs::WriteOption::Flush));
|
||||
g_fs_offset += len;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void InitializeDebugLog() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void DebugLog(const char *prefix, const char *fmt, ...) {
|
||||
AMS_UNUSED(prefix, fmt);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,10 +27,15 @@ namespace ams::dmnt {
|
||||
|
||||
}
|
||||
|
||||
Result DebugProcess::Attach(os::ProcessId process_id) {
|
||||
Result DebugProcess::Attach(os::ProcessId process_id, bool start_process) {
|
||||
/* Attach to the process. */
|
||||
R_TRY(svc::DebugActiveProcess(std::addressof(m_debug_handle), process_id.value));
|
||||
|
||||
/* If necessary, start the process. */
|
||||
if (start_process) {
|
||||
R_ABORT_UNLESS(pm::dmnt::StartProcess(process_id));
|
||||
}
|
||||
|
||||
/* Collect initial information. */
|
||||
R_TRY(this->Start());
|
||||
|
||||
@@ -42,6 +47,10 @@ namespace ams::dmnt {
|
||||
svc::GetProcessId(std::addressof(pid_value), m_debug_handle);
|
||||
|
||||
m_process_id = { pid_value };
|
||||
|
||||
/* Get process info. */
|
||||
this->CollectProcessInfo();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -187,13 +196,25 @@ namespace ams::dmnt {
|
||||
char path[ModuleDefinition::PathLengthMax];
|
||||
} module_path;
|
||||
if (R_SUCCEEDED(this->ReadMemory(std::addressof(module_path), memory_info.base_address + memory_info.size, sizeof(module_path)))) {
|
||||
if (module_path.zero == 0 && module_path.path_length == util::Strnlen(module_path.path, sizeof(module_path.path))) {
|
||||
std::memcpy(module_name, module_path.path, ModuleDefinition::PathLengthMax);
|
||||
if (module_path.zero == 0 && module_path.path_length > 0) {
|
||||
std::memcpy(module_name, module_path.path, std::min<size_t>(ModuleDefinition::PathLengthMax, module_path.path_length));
|
||||
}
|
||||
} else {
|
||||
module_path.path_length = 0;
|
||||
}
|
||||
|
||||
/* Truncate module name. */
|
||||
module_name[ModuleDefinition::PathLengthMax - 1] = 0;
|
||||
|
||||
/* Set default module name start. */
|
||||
module.SetNameStart(0);
|
||||
|
||||
/* Ignore leading directories. */
|
||||
for (size_t i = 0; i < std::min<size_t>(ModuleDefinition::PathLengthMax, module_path.path_length) && module_name[i] != 0; ++i) {
|
||||
if (module_name[i] == '/' || module_name[i] == '\\') {
|
||||
module.SetNameStart(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +233,53 @@ namespace ams::dmnt {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void DebugProcess::CollectProcessInfo() {
|
||||
/* Define helper for getting process info. */
|
||||
auto CollectProcessInfoImpl = [&](os::NativeHandle handle) -> Result {
|
||||
/* Collect all values. */
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_alias_address), svc::InfoType_AliasRegionAddress, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_alias_size), svc::InfoType_AliasRegionSize, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_heap_address), svc::InfoType_HeapRegionAddress, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_heap_size), svc::InfoType_HeapRegionSize, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_aslr_address), svc::InfoType_AslrRegionAddress, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_aslr_size), svc::InfoType_AslrRegionSize, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_stack_address), svc::InfoType_StackRegionAddress, handle, 0));
|
||||
R_TRY(svc::GetInfo(std::addressof(m_process_stack_size), svc::InfoType_StackRegionSize, handle, 0));
|
||||
|
||||
if (m_program_location.program_id == ncm::InvalidProgramId) {
|
||||
R_TRY(svc::GetInfo(std::addressof(m_program_location.program_id.value), svc::InfoType_ProgramId, handle, 0));
|
||||
}
|
||||
|
||||
u64 value;
|
||||
R_TRY(svc::GetInfo(std::addressof(value), svc::InfoType_IsApplication, handle, 0));
|
||||
m_is_application = value != 0;
|
||||
|
||||
return ResultSuccess();
|
||||
};
|
||||
|
||||
/* Get process info/status. */
|
||||
os::NativeHandle process_handle;
|
||||
if (R_FAILED(pm::dmnt::AtmosphereGetProcessInfo(std::addressof(process_handle), std::addressof(m_program_location), std::addressof(m_process_override_status), m_process_id))) {
|
||||
process_handle = os::InvalidNativeHandle;
|
||||
m_program_location = { ncm::InvalidProgramId, };
|
||||
m_process_override_status = {};
|
||||
}
|
||||
ON_SCOPE_EXIT { os::CloseNativeHandle(process_handle); };
|
||||
|
||||
/* Try collecting from our debug handle, then the process handle. */
|
||||
if (R_FAILED(CollectProcessInfoImpl(m_debug_handle)) && R_FAILED(CollectProcessInfoImpl(process_handle))) {
|
||||
m_process_alias_address = 0;
|
||||
m_process_alias_size = 0;
|
||||
m_process_heap_address = 0;
|
||||
m_process_heap_size = 0;
|
||||
m_process_aslr_address = 0;
|
||||
m_process_aslr_size = 0;
|
||||
m_process_stack_address = 0;
|
||||
m_process_stack_size = 0;
|
||||
m_is_application = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result DebugProcess::GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags) {
|
||||
return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags);
|
||||
}
|
||||
@@ -228,6 +296,11 @@ namespace ams::dmnt {
|
||||
return svc::WriteDebugProcessMemory(m_debug_handle, reinterpret_cast<uintptr_t>(src), address, size);
|
||||
}
|
||||
|
||||
Result DebugProcess::QueryMemory(svc::MemoryInfo *out, uintptr_t address) {
|
||||
svc::PageInfo dummy;
|
||||
return svc::QueryDebugProcessMemory(out, std::addressof(dummy), m_debug_handle, address);
|
||||
}
|
||||
|
||||
Result DebugProcess::Continue() {
|
||||
AMS_DMNT2_GDB_LOG_DEBUG("DebugProcess::Continue() all\n");
|
||||
|
||||
@@ -522,5 +595,25 @@ namespace ams::dmnt {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void DebugProcess::GetThreadName(char *dst, u64 thread_id) const {
|
||||
for (size_t i = 0; i < ThreadCountMax; ++i) {
|
||||
if (m_thread_valid[i] && m_thread_ids[i] == thread_id) {
|
||||
if (R_FAILED(osdbg::GetThreadName(dst, std::addressof(m_thread_infos[i])))) {
|
||||
if (m_thread_infos[i]._thread_type != 0) {
|
||||
if (m_thread_infos[i]._thread_type_type == osdbg::ThreadTypeType_Libnx) {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%010lx", reinterpret_cast<uintptr_t>(m_thread_infos[i]._thread_type));
|
||||
} else {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%010lx", reinterpret_cast<uintptr_t>(m_thread_infos[i]._thread_type));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_ID=%lu", thread_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,7 @@ namespace ams::dmnt {
|
||||
u64 m_last_thread_id{};
|
||||
u64 m_thread_id_override{};
|
||||
u64 m_continue_thread_id{};
|
||||
u64 m_preferred_debug_break_thread_id{};
|
||||
GdbSignal m_last_signal{};
|
||||
bool m_stepping{false};
|
||||
bool m_use_hardware_single_step{false};
|
||||
@@ -64,6 +65,17 @@ namespace ams::dmnt {
|
||||
ModuleDefinition m_module_definitions[ModuleCountMax]{};
|
||||
size_t m_module_count{};
|
||||
size_t m_main_module{};
|
||||
u64 m_process_alias_address{};
|
||||
u64 m_process_alias_size{};
|
||||
u64 m_process_heap_address{};
|
||||
u64 m_process_heap_size{};
|
||||
u64 m_process_aslr_address{};
|
||||
u64 m_process_aslr_size{};
|
||||
u64 m_process_stack_address{};
|
||||
u64 m_process_stack_size{};
|
||||
ncm::ProgramLocation m_program_location{};
|
||||
cfg::OverrideStatus m_process_override_status{};
|
||||
bool m_is_application{false};
|
||||
public:
|
||||
DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) {
|
||||
if (svc::IsKernelMesosphere()) {
|
||||
@@ -80,7 +92,8 @@ namespace ams::dmnt {
|
||||
size_t GetModuleCount() const { return m_module_count; }
|
||||
size_t GetMainModuleIndex() const { return m_main_module; }
|
||||
const char *GetModuleName(size_t ix) const { return m_module_definitions[ix].GetName(); }
|
||||
uintptr_t GetBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
|
||||
uintptr_t GetModuleBaseAddress(size_t ix) const { return m_module_definitions[ix].GetAddress(); }
|
||||
uintptr_t GetModuleSize(size_t ix) const { return m_module_definitions[ix].GetSize(); }
|
||||
ProcessStatus GetStatus() const { return m_status; }
|
||||
os::ProcessId GetProcessId() const { return m_process_id; }
|
||||
|
||||
@@ -95,20 +108,37 @@ namespace ams::dmnt {
|
||||
u64 GetLastThreadId();
|
||||
u64 GetThreadIdOverride() { this->GetLastThreadId(); return m_thread_id_override; }
|
||||
|
||||
u64 GetPreferredDebuggerBreakThreadId() { return m_preferred_debug_break_thread_id; }
|
||||
|
||||
void SetLastThreadId(u64 tid) {
|
||||
m_last_thread_id = tid;
|
||||
m_thread_id_override = tid;
|
||||
}
|
||||
|
||||
void SetThreadIdOverride(u64 tid) {
|
||||
m_thread_id_override = tid;
|
||||
m_thread_id_override = tid;
|
||||
m_preferred_debug_break_thread_id = tid;
|
||||
}
|
||||
|
||||
void SetDebugBreaked() {
|
||||
m_status = ProcessStatus_DebugBreak;
|
||||
}
|
||||
|
||||
u64 GetAliasRegionAddress() const { return m_process_alias_address; }
|
||||
u64 GetAliasRegionSize() const { return m_process_alias_size; }
|
||||
u64 GetHeapRegionAddress() const { return m_process_heap_address; }
|
||||
u64 GetHeapRegionSize() const { return m_process_heap_size; }
|
||||
u64 GetAslrRegionAddress() const { return m_process_aslr_address; }
|
||||
u64 GetAslrRegionSize() const { return m_process_aslr_size; }
|
||||
u64 GetStackRegionAddress() const { return m_process_stack_address; }
|
||||
u64 GetStackRegionSize() const { return m_process_stack_size; }
|
||||
|
||||
const ncm::ProgramLocation &GetProgramLocation() const { return m_program_location; }
|
||||
const cfg::OverrideStatus &GetOverrideStatus() const { return m_process_override_status; }
|
||||
|
||||
bool IsApplication() const { return m_is_application; }
|
||||
public:
|
||||
Result Attach(os::ProcessId process_id);
|
||||
Result Attach(os::ProcessId process_id, bool start_process = false);
|
||||
void Detach();
|
||||
|
||||
Result GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags);
|
||||
@@ -117,6 +147,8 @@ namespace ams::dmnt {
|
||||
Result ReadMemory(void *dst, uintptr_t address, size_t size);
|
||||
Result WriteMemory(const void *src, uintptr_t address, size_t size);
|
||||
|
||||
Result QueryMemory(svc::MemoryInfo *out, uintptr_t address);
|
||||
|
||||
Result Continue();
|
||||
Result Continue(u64 thread_id);
|
||||
Result Step();
|
||||
@@ -146,9 +178,13 @@ namespace ams::dmnt {
|
||||
void GetBranchTarget(svc::ThreadContext &ctx, u64 thread_id, u64 ¤t_pc, u64 &target);
|
||||
|
||||
Result CollectModules();
|
||||
|
||||
void GetThreadName(char *dst, u64 thread_id) const;
|
||||
private:
|
||||
Result Start();
|
||||
|
||||
void CollectProcessInfo();
|
||||
|
||||
s32 ThreadCreate(u64 thread_id);
|
||||
void ThreadExit(u64 thread_id);
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ams::dmnt {
|
||||
|
||||
}
|
||||
|
||||
void GdbPacketIo::SendPacket(bool *out_break, const char *src, HtcsSession *session) {
|
||||
void GdbPacketIo::SendPacket(bool *out_break, const char *src, TransportSession *session) {
|
||||
/* Default to not breaked. */
|
||||
*out_break = false;
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace ams::dmnt {
|
||||
}
|
||||
}
|
||||
|
||||
char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session) {
|
||||
char *GdbPacketIo::ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session) {
|
||||
/* Default to not breaked. */
|
||||
*out_break = false;
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "dmnt2_htcs_session.hpp"
|
||||
#include "dmnt2_transport_session.hpp"
|
||||
|
||||
namespace ams::dmnt {
|
||||
|
||||
static constexpr size_t GdbPacketBufferSize = 16_KB;
|
||||
static constexpr size_t GdbPacketBufferSize = 32_KB;
|
||||
|
||||
class GdbPacketIo {
|
||||
private:
|
||||
@@ -30,8 +30,8 @@ namespace ams::dmnt {
|
||||
|
||||
void SetNoAck() { m_no_ack = true; }
|
||||
|
||||
void SendPacket(bool *out_break, const char *src, HtcsSession *session);
|
||||
char *ReceivePacket(bool *out_break, char *dst, size_t size, HtcsSession *session);
|
||||
void SendPacket(bool *out_break, const char *src, TransportSession *session);
|
||||
char *ReceivePacket(bool *out_break, char *dst, size_t size, TransportSession *session);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "dmnt2_debug_log.hpp"
|
||||
#include "dmnt2_transport_layer.hpp"
|
||||
#include "dmnt2_gdb_server.hpp"
|
||||
#include "dmnt2_gdb_server_impl.hpp"
|
||||
|
||||
@@ -36,34 +37,28 @@ namespace ams::dmnt {
|
||||
while (true) {
|
||||
/* Get a socket. */
|
||||
int fd;
|
||||
while ((fd = htcs::Socket()) == -1) {
|
||||
while ((fd = transport::Socket()) == -1) {
|
||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||
}
|
||||
|
||||
/* Ensure we cleanup the socket when we're done with it. */
|
||||
ON_SCOPE_EXIT {
|
||||
htcs::Close(fd);
|
||||
transport::Close(fd);
|
||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||
};
|
||||
|
||||
/* Create a sock addr for our server. */
|
||||
htcs::SockAddrHtcs addr;
|
||||
addr.family = htcs::HTCS_AF_HTCS;
|
||||
addr.peer_name = htcs::GetPeerNameAny();
|
||||
std::strcpy(addr.port_name.name, "iywys@$gdb");
|
||||
|
||||
/* Bind. */
|
||||
if (htcs::Bind(fd, std::addressof(addr)) == -1) {
|
||||
if (transport::Bind(fd, transport::PortName_GdbServer) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Listen on our port. */
|
||||
while (htcs::Listen(fd, 0) == 0) {
|
||||
while (transport::Listen(fd, 0) == 0) {
|
||||
/* Continue accepting clients, so long as we can. */
|
||||
int client_fd;
|
||||
while (true) {
|
||||
/* Try to accept a client. */
|
||||
if (client_fd = htcs::Accept(fd, std::addressof(addr)); client_fd < 0) {
|
||||
if (client_fd = transport::Accept(fd); client_fd < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -77,7 +72,7 @@ namespace ams::dmnt {
|
||||
}
|
||||
|
||||
/* Close the client socket. */
|
||||
htcs::Close(client_fd);
|
||||
transport::Close(client_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,10 +31,11 @@ namespace ams::dmnt {
|
||||
};
|
||||
private:
|
||||
int m_socket;
|
||||
HtcsSession m_session;
|
||||
TransportSession m_session;
|
||||
GdbPacketIo m_packet_io;
|
||||
char *m_receive_packet{nullptr};
|
||||
char *m_reply_packet{nullptr};
|
||||
char *m_reply_cur{nullptr};
|
||||
char *m_reply_end{nullptr};
|
||||
char m_buffer[GdbPacketBufferSize / 2];
|
||||
bool m_killed{false};
|
||||
os::ThreadType m_events_thread;
|
||||
@@ -42,6 +43,7 @@ namespace ams::dmnt {
|
||||
DebugProcess m_debug_process;
|
||||
os::ProcessId m_process_id{os::InvalidProcessId};
|
||||
os::Event m_event;
|
||||
os::ProcessId m_wait_process_id{os::InvalidProcessId};
|
||||
public:
|
||||
GdbServerImpl(int socket, void *thread_stack, size_t stack_size);
|
||||
~GdbServerImpl();
|
||||
@@ -60,7 +62,7 @@ namespace ams::dmnt {
|
||||
static void DebugEventsThreadEntry(void *arg) { static_cast<GdbServerImpl *>(arg)->DebugEventsThread(); }
|
||||
void DebugEventsThread();
|
||||
void ProcessDebugEvents();
|
||||
void SetStopReplyPacket(GdbSignal signal);
|
||||
void AppendStopReplyPacket(GdbSignal signal);
|
||||
private:
|
||||
void D();
|
||||
|
||||
@@ -98,6 +100,7 @@ namespace ams::dmnt {
|
||||
|
||||
void qAttached();
|
||||
void qC();
|
||||
void qRcmd();
|
||||
void qSupported();
|
||||
void qXfer();
|
||||
void qXferFeaturesRead();
|
||||
|
||||
@@ -16,15 +16,22 @@
|
||||
#include <stratosphere.hpp>
|
||||
#include "dmnt2_debug_log.hpp"
|
||||
#include "dmnt2_gdb_server.hpp"
|
||||
#include "dmnt2_transport_layer.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace dmnt {
|
||||
namespace {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(0x40) constinit u8 g_htcs_buffer[4_KB];
|
||||
bool IsHtcEnabled() {
|
||||
u8 enable_htc = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_htc), sizeof(enable_htc), "atmosphere", "enable_htc");
|
||||
return enable_htc != 0;
|
||||
}
|
||||
|
||||
bool IsStandaloneGdbstubEnabled() {
|
||||
u8 enable_gdbstub = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(enable_gdbstub), sizeof(enable_gdbstub), "atmosphere", "enable_standalone_gdbstub");
|
||||
return enable_gdbstub != 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,6 +42,9 @@ namespace ams {
|
||||
/* Initialize our connection to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
/* Initialize other services we need. */
|
||||
R_ABORT_UNLESS(pmdmntInitialize());
|
||||
|
||||
/* Verify that we can sanely execute. */
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
@@ -50,11 +60,23 @@ namespace ams {
|
||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(dmnt, Main));
|
||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(dmnt, Main));
|
||||
|
||||
/* Initialize htcs. */
|
||||
constexpr auto HtcsSocketCountMax = 8;
|
||||
const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax);
|
||||
AMS_ABORT_UNLESS(sizeof(dmnt::g_htcs_buffer) >= buffer_size);
|
||||
htcs::InitializeForSystem(dmnt::g_htcs_buffer, buffer_size, HtcsSocketCountMax);
|
||||
bool use_htcs = false, use_tcp = false;
|
||||
{
|
||||
R_ABORT_UNLESS(::setsysInitialize());
|
||||
ON_SCOPE_EXIT { ::setsysExit(); };
|
||||
|
||||
use_htcs = IsHtcEnabled();
|
||||
use_tcp = IsStandaloneGdbstubEnabled();
|
||||
}
|
||||
|
||||
/* Initialize transport layer. */
|
||||
if (use_htcs) {
|
||||
dmnt::transport::InitializeByHtcs();
|
||||
} else if (use_tcp) {
|
||||
dmnt::transport::InitializeByTcp();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize debug log thread. */
|
||||
dmnt::InitializeDebugLog();
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace ams::dmnt {
|
||||
char m_name[PathLengthMax];
|
||||
u64 m_address;
|
||||
u64 m_size;
|
||||
size_t m_name_start;
|
||||
public:
|
||||
constexpr ModuleDefinition() : m_name(), m_address(), m_size() { /* ... */ }
|
||||
constexpr ~ModuleDefinition() { /* ... */ }
|
||||
@@ -34,9 +35,10 @@ namespace ams::dmnt {
|
||||
constexpr ModuleDefinition &operator=(const ModuleDefinition &rhs) = default;
|
||||
|
||||
constexpr void Reset() {
|
||||
m_name[0] = 0;
|
||||
m_address = 0;
|
||||
m_size = 0;
|
||||
m_name[0] = 0;
|
||||
m_address = 0;
|
||||
m_size = 0;
|
||||
m_name_start = 0;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const ModuleDefinition &rhs) const {
|
||||
@@ -48,7 +50,7 @@ namespace ams::dmnt {
|
||||
}
|
||||
|
||||
constexpr char *GetNameBuffer() { return m_name; }
|
||||
constexpr const char *GetName() const { return m_name; }
|
||||
constexpr const char *GetName() const { return m_name + m_name_start; }
|
||||
|
||||
constexpr u64 GetAddress() const { return m_address; }
|
||||
constexpr u64 GetSize() const { return m_size; }
|
||||
@@ -57,6 +59,10 @@ namespace ams::dmnt {
|
||||
m_address = address;
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
constexpr void SetNameStart(size_t offset) {
|
||||
m_name_start = offset;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user