Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -1,4 +1,59 @@
|
|||||||
# Changelog
|
# 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
|
## 1.2.0
|
||||||
+ `boot` was updated to reflect the latest official behavior for display/battery management.
|
+ `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.
|
+ 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]
|
[subrepo]
|
||||||
remote = https://github.com/m4xw/emuMMC
|
remote = https://github.com/m4xw/emuMMC
|
||||||
branch = develop
|
branch = develop
|
||||||
commit = f66087313546161a000ee196a788f0626caf80fa
|
commit = a9d56959460fc794ce2cb6405402c25a3e89c47f
|
||||||
parent = 38f9a76ba028995ed3274da3a45b0254f09d1f59
|
parent = ff719641396c635b735873fb2b020c910f768a04
|
||||||
method = rebase
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
16
emummc/README.md
vendored
16
emummc/README.md
vendored
@@ -1,21 +1,21 @@
|
|||||||
# emuMMC
|
# 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
|
### Supported Horizon Versions
|
||||||
**1.0.0 - 13.0.0**
|
**1.0.0 - 13.1.0**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Arbitrary SDMMC backend selection
|
* Arbitrary SDMMC backend selection
|
||||||
**This allows loading eMMC from SD or even SD from eMMC**
|
**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!**
|
**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)**
|
**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**
|
**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!**
|
**No 8 char length restriction!**
|
||||||
* exosphere based context configuration
|
* exosphere based context configuration
|
||||||
**This includes full support for multiple emuMMC images**
|
**This includes full support for multiple emuMMC images**
|
||||||
|
|
||||||
## Compiling
|
## 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/1203_exfat.h"
|
||||||
#include "offsets/1300.h"
|
#include "offsets/1300.h"
|
||||||
#include "offsets/1300_exfat.h"
|
#include "offsets/1300_exfat.h"
|
||||||
|
#include "offsets/1310.h"
|
||||||
|
#include "offsets/1310_exfat.h"
|
||||||
#include "../utils/fatal.h"
|
#include "../utils/fatal.h"
|
||||||
|
|
||||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
#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(_1203_EXFAT);
|
||||||
DEFINE_OFFSET_STRUCT(_1300);
|
DEFINE_OFFSET_STRUCT(_1300);
|
||||||
DEFINE_OFFSET_STRUCT(_1300_EXFAT);
|
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) {
|
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
@@ -210,6 +214,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
|||||||
return &(GET_OFFSET_STRUCT_NAME(_1300));
|
return &(GET_OFFSET_STRUCT_NAME(_1300));
|
||||||
case FS_VER_13_0_0_EXFAT:
|
case FS_VER_13_0_0_EXFAT:
|
||||||
return &(GET_OFFSET_STRUCT_NAME(_1300_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:
|
default:
|
||||||
fatal_abort(Fatal_UnknownVersion);
|
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,
|
||||||
FS_VER_13_0_0_EXFAT,
|
FS_VER_13_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_13_1_0,
|
||||||
|
FS_VER_13_1_0_EXFAT,
|
||||||
|
|
||||||
FS_VER_MAX,
|
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,
|
||||||
FsVersion_13_0_0_Exfat,
|
FsVersion_13_0_0_Exfat,
|
||||||
|
|
||||||
|
FsVersion_13_1_0,
|
||||||
|
FsVersion_13_1_0_Exfat,
|
||||||
|
|
||||||
FsVersion_Count,
|
FsVersion_Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -215,6 +218,9 @@ namespace ams::nxboot {
|
|||||||
|
|
||||||
{ 0x7D, 0x20, 0x05, 0x47, 0x17, 0x8A, 0x83, 0x6A }, /* FsVersion_13_0_0 */
|
{ 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 */
|
{ 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) {
|
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, 0x159119, NogcPatch0, sizeof(NogcPatch0));
|
||||||
AddPatch(fs_meta, 0x1426D0, NogcPatch1, sizeof(NogcPatch1));
|
AddPatch(fs_meta, 0x1426D0, NogcPatch1, sizeof(NogcPatch1));
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = cf765c0946cc5c828364ae6bfccddc4041304f28
|
commit = 4d0f1b79246d7d2c19219bc7da153fdfef165f57
|
||||||
parent = 8634ea0f7c4f0e68adf2dfaaddc6ae1e225c4fc2
|
parent = 94e18b8c93e6013aaa647d31d54cb3f06e391a46
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
5
libraries/config/board/qemu/virt/board.mk
Normal file
5
libraries/config/board/qemu/virt/board.mk
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_QEMU_VIRT -D__SWITCH__
|
||||||
|
export ATMOSPHERE_SETTINGS +=
|
||||||
|
export ATMOSPHERE_CFLAGS +=
|
||||||
|
export ATMOSPHERE_CXXFLAGS +=
|
||||||
|
export ATMOSPHERE_ASFLAGS +=
|
||||||
@@ -52,6 +52,21 @@ export ATMOSPHERE_OS_NAME := horizon
|
|||||||
export ATMOSPHERE_CPU_EXTENSIONS :=
|
export ATMOSPHERE_CPU_EXTENSIONS :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
else ifeq ($(ATMOSPHERE_BOARD),qemu-virt)
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
|
||||||
|
export ATMOSPHERE_ARCH_DIR := arm64
|
||||||
|
export ATMOSPHERE_BOARD_DIR := qemu/virt
|
||||||
|
export ATMOSPHERE_OS_DIR := horizon
|
||||||
|
|
||||||
|
export ATMOSPHERE_ARCH_NAME := arm64
|
||||||
|
export ATMOSPHERE_BOARD_NAME := qemu_virt
|
||||||
|
export ATMOSPHERE_OS_NAME := horizon
|
||||||
|
|
||||||
|
export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
|
||||||
|
endif
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
|
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
|
||||||
|
|||||||
@@ -104,6 +104,18 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \
|
|||||||
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
|
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
|
||||||
))
|
))
|
||||||
|
|
||||||
|
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, $(TARGET)_qemu_virt.a, \
|
||||||
|
ATMOSPHERE_BUILD_SETTINGS="" \
|
||||||
|
))
|
||||||
|
|
||||||
|
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, $(TARGET)_qemu_virt_debug.a, \
|
||||||
|
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \
|
||||||
|
))
|
||||||
|
|
||||||
|
$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, $(TARGET)_qemu_virt_audit.a, \
|
||||||
|
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
|
||||||
|
))
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk
|
-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk
|
||||||
|
|||||||
@@ -93,3 +93,4 @@
|
|||||||
|
|
||||||
/* Deferred includes. */
|
/* Deferred includes. */
|
||||||
#include <mesosphere/kern_k_auto_object_impls.hpp>
|
#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. */
|
/* Invalidate the entire tlb. */
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
cpu::InvalidateEntireTlb();
|
cpu::InvalidateEntireTlbInnerShareable();
|
||||||
|
|
||||||
/* Copy data, if we should. */
|
/* Copy data, if we should. */
|
||||||
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
|
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 we don't already have an L2 table, we need to make a new one. */
|
||||||
if (!l1_entry->IsTable()) {
|
if (!l1_entry->IsTable()) {
|
||||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||||
ClearNewPageTable(new_table);
|
|
||||||
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
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) {
|
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
|
||||||
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
|
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
|
||||||
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
l2_entry[i] = L2PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
|
||||||
|
|
||||||
virt_addr += L2BlockSize;
|
virt_addr += L2BlockSize;
|
||||||
phys_addr += L2BlockSize;
|
phys_addr += L2BlockSize;
|
||||||
size -= L2BlockSize;
|
size -= L2BlockSize;
|
||||||
}
|
}
|
||||||
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
continue;
|
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 we don't already have an L3 table, we need to make a new one. */
|
||||||
if (!l2_entry->IsTable()) {
|
if (!l2_entry->IsTable()) {
|
||||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||||
ClearNewPageTable(new_table);
|
|
||||||
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
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) {
|
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
|
||||||
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
|
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
|
||||||
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
l3_entry[i] = L3PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, attr, PageTableEntry::SoftwareReservedBit_None, true);
|
||||||
cpu::DataSynchronizationBarrierInnerShareable();
|
|
||||||
|
|
||||||
virt_addr += L3BlockSize;
|
virt_addr += L3BlockSize;
|
||||||
phys_addr += L3BlockSize;
|
phys_addr += L3BlockSize;
|
||||||
size -= L3BlockSize;
|
size -= L3BlockSize;
|
||||||
}
|
}
|
||||||
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
/* TODO: Different header for this? */
|
/* TODO: Different header for this? */
|
||||||
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
|
#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 */
|
/* 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_SIZE 0x30
|
||||||
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
|
|
||||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||||
constexpr inline size_t NumCores = 4;
|
constexpr inline size_t NumCores = 4;
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
constexpr inline size_t NumCores = 4;
|
||||||
#else
|
#else
|
||||||
#error "Unknown Board for cpu::NumCores"
|
#error "Unknown Board for cpu::NumCores"
|
||||||
#endif
|
#endif
|
||||||
@@ -50,10 +52,19 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
__asm__ __volatile__("dmb sy" ::: "memory");
|
__asm__ __volatile__("dmb sy" ::: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void DataMemoryBarrierInnerShareable() {
|
||||||
|
__asm__ __volatile__("dmb ish" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void InstructionMemoryBarrier() {
|
ALWAYS_INLINE void InstructionMemoryBarrier() {
|
||||||
__asm__ __volatile__("isb" ::: "memory");
|
__asm__ __volatile__("isb" ::: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void EnsureInstructionConsistencyInnerShareable() {
|
||||||
|
DataSynchronizationBarrierInnerShareable();
|
||||||
|
InstructionMemoryBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void EnsureInstructionConsistency() {
|
ALWAYS_INLINE void EnsureInstructionConsistency() {
|
||||||
DataSynchronizationBarrier();
|
DataSynchronizationBarrier();
|
||||||
InstructionMemoryBarrier();
|
InstructionMemoryBarrier();
|
||||||
@@ -171,7 +182,6 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
NOINLINE void SynchronizeAllCores();
|
NOINLINE void SynchronizeAllCores();
|
||||||
|
|
||||||
/* Cache management helpers. */
|
/* Cache management helpers. */
|
||||||
void ClearPageToZeroImpl(void *);
|
|
||||||
void StoreEntireCacheForInit();
|
void StoreEntireCacheForInit();
|
||||||
void FlushEntireCacheForInit();
|
void FlushEntireCacheForInit();
|
||||||
|
|
||||||
@@ -184,10 +194,16 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
|
|
||||||
void InvalidateEntireInstructionCache();
|
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(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
|
||||||
MESOSPHERE_ASSERT(page != nullptr);
|
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) {
|
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
|
||||||
@@ -207,6 +223,11 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
EnsureInstructionConsistency();
|
EnsureInstructionConsistency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void InvalidateEntireTlbInnerShareable() {
|
||||||
|
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||||
|
EnsureInstructionConsistencyInnerShareable();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
|
||||||
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
|
||||||
DataSynchronizationBarrier();
|
DataSynchronizationBarrier();
|
||||||
|
|||||||
@@ -105,35 +105,48 @@ namespace ams::kern::arch::arm64 {
|
|||||||
Result UnbindLocal(s32 irq);
|
Result UnbindLocal(s32 irq);
|
||||||
Result ClearGlobal(s32 irq);
|
Result ClearGlobal(s32 irq);
|
||||||
Result ClearLocal(s32 irq);
|
Result ClearLocal(s32 irq);
|
||||||
public:
|
private:
|
||||||
static ALWAYS_INLINE u32 DisableInterrupts() {
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledState() {
|
||||||
u64 intr_state;
|
u64 intr_state;
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
||||||
"msr daifset, #2"
|
"ubfx %[intr_state], %[intr_state], #7, #1"
|
||||||
: [intr_state]"=r"(intr_state)
|
: [intr_state]"=r"(intr_state)
|
||||||
:: "memory");
|
:: "memory");
|
||||||
return intr_state;
|
return intr_state;
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
|
static ALWAYS_INLINE void EnableInterrupts() {
|
||||||
|
__asm__ __volatile__("msr daifclr, #2" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE u32 EnableInterrupts() {
|
static ALWAYS_INLINE void DisableInterrupts() {
|
||||||
u64 intr_state;
|
__asm__ __volatile__("msr daifset, #2" ::: "memory");
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif\n"
|
}
|
||||||
"msr daifclr, #2"
|
|
||||||
: [intr_state]"=r"(intr_state)
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndDisableInterrupts() {
|
||||||
:: "memory");
|
const auto intr_state = GetInterruptsEnabledState();
|
||||||
|
DisableInterrupts();
|
||||||
|
return intr_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static ALWAYS_INLINE u32 GetInterruptsEnabledStateAndEnableInterrupts() {
|
||||||
|
const auto intr_state = GetInterruptsEnabledState();
|
||||||
|
EnableInterrupts();
|
||||||
return intr_state;
|
return intr_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
|
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
|
||||||
u64 cur_state;
|
u64 tmp;
|
||||||
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
|
__asm__ __volatile__("mrs %[tmp], daif\n"
|
||||||
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
|
"bfi %[tmp], %[intr_state], #7, #1\n"
|
||||||
|
"msr daif, %[tmp]"
|
||||||
|
: [tmp]"=&r"(tmp)
|
||||||
|
: [intr_state]"r"(intr_state)
|
||||||
|
: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE bool AreInterruptsEnabled() {
|
static ALWAYS_INLINE bool AreInterruptsEnabled() {
|
||||||
u64 intr_state;
|
return GetInterruptsEnabledState() == 0;
|
||||||
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
|
|
||||||
return (intr_state & 0x80) == 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
KInterruptName_SecurePhysicalTimer = 29,
|
KInterruptName_SecurePhysicalTimer = 29,
|
||||||
KInterruptName_NonSecurePhysicalTimer = 30,
|
KInterruptName_NonSecurePhysicalTimer = 30,
|
||||||
KInterruptName_LegacyNIrq = 31,
|
KInterruptName_LegacyNIrq = 31,
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
KInterruptName_VirtualTimer = 27,
|
||||||
|
KInterruptName_SecurePhysicalTimer = 29,
|
||||||
|
KInterruptName_NonSecurePhysicalTimer = 30,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||||
|
|||||||
@@ -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);
|
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();
|
cpu::DataSynchronizationBarrierInnerShareable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ClearPageTable(KVirtualAddress table) {
|
static ALWAYS_INLINE void ClearPageTable(KVirtualAddress table) {
|
||||||
cpu::ClearPageToZero(GetVoidPointer(table));
|
cpu::ClearPageToZero(GetVoidPointer(table));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTableUpdated() const {
|
ALWAYS_INLINE void OnTableUpdated() const {
|
||||||
cpu::InvalidateTlbByAsid(m_asid);
|
cpu::InvalidateTlbByAsid(m_asid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnKernelTableUpdated() const {
|
ALWAYS_INLINE void OnKernelTableUpdated() const {
|
||||||
cpu::InvalidateEntireTlbDataOnly();
|
cpu::InvalidateEntireTlbDataOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
ALWAYS_INLINE void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
|
||||||
cpu::InvalidateTlbByVaDataOnly(virt_addr);
|
cpu::InvalidateTlbByVaDataOnly(virt_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoteUpdated() const {
|
ALWAYS_INLINE void NoteUpdated() const {
|
||||||
cpu::DataSynchronizationBarrier();
|
cpu::DataSynchronizationBarrier();
|
||||||
|
|
||||||
if (this->IsKernel()) {
|
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());
|
MESOSPHERE_ASSERT(this->IsKernel());
|
||||||
|
|
||||||
cpu::DataSynchronizationBarrier();
|
cpu::DataSynchronizationBarrier();
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
/* Select L1 cache. */
|
/* Select L1 cache. */
|
||||||
cpu::SetCsselrEl1(0);
|
cpu::SetCsselrEl1(0);
|
||||||
|
cpu::InstructionMemoryBarrier();
|
||||||
|
|
||||||
/* Check that the L1 cache is not direct-mapped. */
|
/* Check that the L1 cache is not direct-mapped. */
|
||||||
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;
|
return cpu::CacheSizeIdRegisterAccessor().GetAssociativity() != 0;
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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_select_cpu.hpp>
|
||||||
|
#include <mesosphere/kern_select_interrupt_manager.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern::arch::arm64::smc {
|
||||||
|
|
||||||
|
template<int SmcId, bool DisableInterrupt>
|
||||||
|
void SecureMonitorCall(u64 *buf) {
|
||||||
|
/* Load arguments into registers. */
|
||||||
|
register u64 x0 asm("x0") = buf[0];
|
||||||
|
register u64 x1 asm("x1") = buf[1];
|
||||||
|
register u64 x2 asm("x2") = buf[2];
|
||||||
|
register u64 x3 asm("x3") = buf[3];
|
||||||
|
register u64 x4 asm("x4") = buf[4];
|
||||||
|
register u64 x5 asm("x5") = buf[5];
|
||||||
|
register u64 x6 asm("x6") = buf[6];
|
||||||
|
register u64 x7 asm("x7") = buf[7];
|
||||||
|
|
||||||
|
/* Perform the call. */
|
||||||
|
if constexpr (DisableInterrupt) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
|
|
||||||
|
/* Backup the current thread pointer. */
|
||||||
|
const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
|
||||||
|
|
||||||
|
__asm__ __volatile__("smc %c[smc_id]"
|
||||||
|
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
||||||
|
: [smc_id]"i"(SmcId)
|
||||||
|
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Restore the current thread pointer into X18. */
|
||||||
|
cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
|
||||||
|
} else {
|
||||||
|
/* Backup the current thread pointer. */
|
||||||
|
const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
|
||||||
|
|
||||||
|
__asm__ __volatile__("smc %c[smc_id]"
|
||||||
|
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
||||||
|
: [smc_id]"i"(SmcId)
|
||||||
|
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Restore the current thread pointer into X18. */
|
||||||
|
cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store arguments to output. */
|
||||||
|
buf[0] = x0;
|
||||||
|
buf[1] = x1;
|
||||||
|
buf[2] = x2;
|
||||||
|
buf[3] = x3;
|
||||||
|
buf[4] = x4;
|
||||||
|
buf[5] = x5;
|
||||||
|
buf[6] = x6;
|
||||||
|
buf[7] = x7;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PsciFunction {
|
||||||
|
PsciFunction_CpuSuspend = 0xC4000001,
|
||||||
|
PsciFunction_CpuOff = 0x84000002,
|
||||||
|
PsciFunction_CpuOn = 0xC4000003,
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int SmcId, bool DisableInterrupt>
|
||||||
|
u64 PsciCall(PsciFunction function, u64 x1 = 0, u64 x2 = 0, u64 x3 = 0, u64 x4 = 0, u64 x5 = 0, u64 x6 = 0, u64 x7 = 0) {
|
||||||
|
ams::svc::lp64::SecureMonitorArguments args = { { function, x1, x2, x3, x4, x5, x6, x7 } };
|
||||||
|
|
||||||
|
SecureMonitorCall<SmcId, DisableInterrupt>(args.r);
|
||||||
|
|
||||||
|
return args.r[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int SmcId, bool DisableInterrupt>
|
||||||
|
u64 CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||||
|
return PsciCall<SmcId, DisableInterrupt>(PsciFunction_CpuOn, core_id, entrypoint, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -44,13 +44,13 @@ namespace ams::kern::board::generic {
|
|||||||
return ams::kern::svc::ResultNotImplemented();
|
return ams::kern::svc::ResultNotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ALWAYS_INLINE Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
|
Result ALWAYS_INLINE Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
|
||||||
MESOSPHERE_UNUSED(out_mapped_size, pg, device_address, device_perm, refresh_mappings);
|
MESOSPHERE_UNUSED(page_table, process_address, size, device_address, device_perm, is_aligned);
|
||||||
return ams::kern::svc::ResultNotImplemented();
|
return ams::kern::svc::ResultNotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ALWAYS_INLINE Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
|
Result ALWAYS_INLINE Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) {
|
||||||
MESOSPHERE_UNUSED(pg, device_address);
|
MESOSPHERE_UNUSED(page_table, process_address, size, device_address);
|
||||||
return ams::kern::svc::ResultNotImplemented();
|
return ams::kern::svc::ResultNotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_typed_address.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
|
constexpr inline KPhysicalAddress MainMemoryAddress = 0x80000000;
|
||||||
|
|
||||||
constexpr inline size_t MainMemorySize = 4_GB;
|
constexpr inline size_t MainMemorySize = 4_GB;
|
||||||
constexpr inline size_t MainMemorySizeMax = 8_GB;
|
constexpr inline size_t MainMemorySizeMax = 8_GB;
|
||||||
|
|
||||||
|
|||||||
@@ -15,23 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_system_control_base.hpp>
|
||||||
namespace ams::kern {
|
|
||||||
|
|
||||||
struct InitialProcessBinaryLayout;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ams::kern::board::nintendo::nx {
|
namespace ams::kern::board::nintendo::nx {
|
||||||
|
|
||||||
class KSystemControl {
|
class KSystemControl : public KSystemControlBase {
|
||||||
public:
|
public:
|
||||||
class Init {
|
class Init : public KSystemControlBase::Init {
|
||||||
public:
|
public:
|
||||||
/* Initialization. */
|
/* Initialization. */
|
||||||
|
static size_t GetRealMemorySize();
|
||||||
static size_t GetIntendedMemorySize();
|
static size_t GetIntendedMemorySize();
|
||||||
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
|
|
||||||
static void GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out);
|
|
||||||
static bool ShouldIncreaseThreadResourceLimit();
|
static bool ShouldIncreaseThreadResourceLimit();
|
||||||
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||||
static size_t GetApplicationPoolSize();
|
static size_t GetApplicationPoolSize();
|
||||||
@@ -40,7 +34,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
static u8 GetDebugLogUartPort();
|
static u8 GetDebugLogUartPort();
|
||||||
|
|
||||||
/* Randomness. */
|
/* Randomness. */
|
||||||
static void GenerateRandomBytes(void *dst, size_t size);
|
static void GenerateRandom(u64 *dst, size_t count);
|
||||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
@@ -50,7 +44,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
static NOINLINE u32 GetCreateProcessMemoryPool();
|
static NOINLINE u32 GetCreateProcessMemoryPool();
|
||||||
|
|
||||||
/* Randomness. */
|
/* Randomness. */
|
||||||
static void GenerateRandomBytes(void *dst, size_t size);
|
static void GenerateRandom(u64 *dst, size_t count);
|
||||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
static u64 GenerateRandomU64();
|
static u64 GenerateRandomU64();
|
||||||
|
|
||||||
@@ -58,23 +52,12 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
||||||
static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
||||||
|
|
||||||
static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
|
|
||||||
u32 v;
|
|
||||||
ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ALWAYS_INLINE void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
|
|
||||||
u32 v;
|
|
||||||
ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Power management. */
|
/* Power management. */
|
||||||
static void SleepSystem();
|
static void SleepSystem();
|
||||||
static NORETURN void StopSystem(void *arg = nullptr);
|
static NORETURN void StopSystem(void *arg = nullptr);
|
||||||
|
|
||||||
/* User access. */
|
/* User access. */
|
||||||
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
static void CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
|
||||||
/* Secure Memory. */
|
/* Secure Memory. */
|
||||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
namespace ams::kern::board::qemu::virt::impl::cpu {
|
||||||
|
|
||||||
|
/* Virtual to Physical core map. */
|
||||||
|
constexpr inline const s32 VirtualToPhysicalCoreMap[BITSIZEOF(u64)] = {
|
||||||
|
0, 1, 2, 3, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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_typed_address.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
constexpr inline KPhysicalAddress MainMemoryAddress = 0x40000000;
|
||||||
|
|
||||||
|
constexpr inline size_t MainMemorySize = 4_GB;
|
||||||
|
constexpr inline size_t MainMemorySizeMax = 8_GB;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* All architectures must define NumBoardDeviceRegions. */
|
||||||
|
constexpr inline const auto NumBoardDeviceRegions = 0;
|
||||||
|
/* UNUSED: .Derive(NumBoardDeviceRegions, 0); */
|
||||||
|
|
||||||
@@ -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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_system_control_base.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern::board::qemu::virt {
|
||||||
|
|
||||||
|
class KSystemControl : public KSystemControlBase {
|
||||||
|
public:
|
||||||
|
/* User access. */
|
||||||
|
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,4 +38,13 @@
|
|||||||
/* at the cost of storing class tokens inside the class object. */
|
/* at the cost of storing class tokens inside the class object. */
|
||||||
/* However, as of (10/16/2021) KAutoObject has an unused class member */
|
/* However, as of (10/16/2021) KAutoObject has an unused class member */
|
||||||
/* of the right side, and so this does not actually cost any space. */
|
/* of the right side, and so this does not actually cost any space. */
|
||||||
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST
|
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||||
|
//#define MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
|
||||||
|
#else
|
||||||
|
#define MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
|
||||||
|
#endif
|
||||||
@@ -43,6 +43,8 @@ namespace ams::kern {
|
|||||||
|
|
||||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
||||||
#define MESOSPHERE_DEBUG_LOG_USE_UART
|
#define MESOSPHERE_DEBUG_LOG_USE_UART
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
#define MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING
|
||||||
#else
|
#else
|
||||||
#error "Unknown board for Default Debug Log Source"
|
#error "Unknown board for Default Debug Log Source"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -82,7 +82,11 @@ namespace ams::kern {
|
|||||||
DEFINE_FIELD(Index, Mask, 3);
|
DEFINE_FIELD(Index, Mask, 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES)
|
||||||
|
static constexpr u64 PhysicalMapAllowedMask = (1ul << 40) - 1;
|
||||||
|
#else
|
||||||
static constexpr u64 PhysicalMapAllowedMask = (1ul << 36) - 1;
|
static constexpr u64 PhysicalMapAllowedMask = (1ul << 36) - 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct MapRange {
|
struct MapRange {
|
||||||
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
|
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
|
||||||
@@ -94,9 +98,15 @@ namespace ams::kern {
|
|||||||
struct MapRangeSize {
|
struct MapRangeSize {
|
||||||
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
|
using IdBits = Field<0, CapabilityId<CapabilityType::MapRange> + 1>;
|
||||||
|
|
||||||
DEFINE_FIELD(Pages, IdBits, 20);
|
DEFINE_FIELD(Pages, IdBits, 20);
|
||||||
|
|
||||||
|
#if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES)
|
||||||
|
DEFINE_FIELD(AddressHigh, Pages, 4);
|
||||||
|
DEFINE_FIELD(Normal, AddressHigh, 1, bool);
|
||||||
|
#else
|
||||||
DEFINE_FIELD(Reserved, Pages, 4);
|
DEFINE_FIELD(Reserved, Pages, 4);
|
||||||
DEFINE_FIELD(Normal, Reserved, 1, bool);
|
DEFINE_FIELD(Normal, Reserved, 1, bool);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MapIoPage {
|
struct MapIoPage {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace ams::kern {
|
|||||||
return m_slab_heap->Allocate(m_page_allocator);
|
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);
|
m_slab_heap->Free(t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||||
#include <mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp>
|
#include <mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
#include <mesosphere/board/qemu/virt/kern_k_memory_layout.hpp>
|
||||||
#else
|
#else
|
||||||
#error "Unknown board for KMemoryLayout"
|
#error "Unknown board for KMemoryLayout"
|
||||||
#endif
|
#endif
|
||||||
@@ -210,13 +212,17 @@ namespace ams::kern {
|
|||||||
static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); }
|
static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); }
|
||||||
static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); }
|
static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); }
|
||||||
|
|
||||||
static NOINLINE auto GetKernelPoolManagementRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolManagement); }
|
|
||||||
static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition); }
|
static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition); }
|
||||||
|
static NOINLINE auto GetKernelPoolManagementRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolManagement); }
|
||||||
static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool); }
|
static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool); }
|
||||||
static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool); }
|
static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool); }
|
||||||
static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool); }
|
static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool); }
|
||||||
static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool); }
|
static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool); }
|
||||||
|
|
||||||
|
static NOINLINE bool HasKernelSystemNonSecurePoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramSystemNonSecurePool) != nullptr; }
|
||||||
|
static NOINLINE bool HasKernelAppletPoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramAppletPool) != nullptr; }
|
||||||
|
static NOINLINE bool HasKernelApplicationPoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramApplicationPool) != nullptr; }
|
||||||
|
|
||||||
static NOINLINE auto GetKernelTraceBufferRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelTraceBuffer); }
|
static NOINLINE auto GetKernelTraceBufferRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelTraceBuffer); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -48,39 +48,20 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t SelectRandomBit(u64 bitmap) {
|
size_t SelectRandomBit(u64 bitmap) {
|
||||||
u64 selected = 0;
|
u64 selected = 0;
|
||||||
|
|
||||||
u64 cur_num_bits = BITSIZEOF(bitmap) / 2;
|
for (size_t cur_num_bits = BITSIZEOF(bitmap) / 2; cur_num_bits != 0; cur_num_bits /= 2) {
|
||||||
u64 cur_mask = (1ull << cur_num_bits) - 1;
|
const u64 high = (bitmap >> cur_num_bits);
|
||||||
|
const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits)));
|
||||||
|
|
||||||
while (cur_num_bits) {
|
/* Choose high if we have high and (don't have low or select high randomly). */
|
||||||
const u64 low = (bitmap >> 0) & cur_mask;
|
if (high && (low == 0 || this->GenerateRandomBit())) {
|
||||||
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
|
|
||||||
|
|
||||||
bool choose_low;
|
|
||||||
if (high == 0) {
|
|
||||||
/* If only low val is set, choose low. */
|
|
||||||
choose_low = true;
|
|
||||||
} else if (low == 0) {
|
|
||||||
/* If only high val is set, choose high. */
|
|
||||||
choose_low = false;
|
|
||||||
} else {
|
|
||||||
/* If both are set, choose random. */
|
|
||||||
choose_low = this->GenerateRandomBit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we chose low, proceed with low. */
|
|
||||||
if (choose_low) {
|
|
||||||
bitmap = low;
|
|
||||||
selected += 0;
|
|
||||||
} else {
|
|
||||||
bitmap = high;
|
bitmap = high;
|
||||||
selected += cur_num_bits;
|
selected += cur_num_bits;
|
||||||
|
} else {
|
||||||
|
bitmap = low;
|
||||||
|
selected += 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Proceed. */
|
|
||||||
cur_num_bits /= 2;
|
|
||||||
cur_mask >>= cur_num_bits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return selected;
|
return selected;
|
||||||
|
|||||||
@@ -211,18 +211,6 @@ namespace ams::kern {
|
|||||||
static consteval bool ValidateAssemblyOffsets();
|
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> {
|
class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> {
|
||||||
public:
|
public:
|
||||||
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }
|
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. */
|
/* Release an instance of the lock. */
|
||||||
if ((--m_lock_count) == 0) {
|
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. */
|
/* We're no longer going to hold the lock. Take note of what cores need scheduling. */
|
||||||
const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads();
|
const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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_spin_lock.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
struct InitialProcessBinaryLayout;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KSystemControlBase {
|
||||||
|
protected:
|
||||||
|
/* Nintendo uses std::mt19937_t for randomness. */
|
||||||
|
/* To save space (and because mt19337_t isn't secure anyway), */
|
||||||
|
/* We will use TinyMT. */
|
||||||
|
static constinit inline bool s_initialized_random_generator;
|
||||||
|
static constinit inline util::TinyMT s_random_generator{util::ConstantInitialize};
|
||||||
|
static constinit inline KSpinLock s_random_lock;
|
||||||
|
public:
|
||||||
|
class Init {
|
||||||
|
public:
|
||||||
|
/* Initialization. */
|
||||||
|
static size_t GetRealMemorySize();
|
||||||
|
static size_t GetIntendedMemorySize();
|
||||||
|
static KPhysicalAddress GetKernelPhysicalBaseAddress(KPhysicalAddress base_address);
|
||||||
|
static void GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out);
|
||||||
|
static bool ShouldIncreaseThreadResourceLimit();
|
||||||
|
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||||
|
static size_t GetApplicationPoolSize();
|
||||||
|
static size_t GetAppletPoolSize();
|
||||||
|
static size_t GetMinimumNonSecureSystemPoolSize();
|
||||||
|
static u8 GetDebugLogUartPort();
|
||||||
|
|
||||||
|
/* Randomness. */
|
||||||
|
static void GenerateRandom(u64 *dst, size_t count);
|
||||||
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
/* Initialization. */
|
||||||
|
static NOINLINE void InitializePhase1(bool skip_target_system = false);
|
||||||
|
static NOINLINE void InitializePhase2();
|
||||||
|
static NOINLINE u32 GetCreateProcessMemoryPool();
|
||||||
|
|
||||||
|
/* Randomness. */
|
||||||
|
static void GenerateRandom(u64 *dst, size_t count);
|
||||||
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
|
static u64 GenerateRandomU64();
|
||||||
|
|
||||||
|
/* Register access Access. */
|
||||||
|
static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
||||||
|
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
||||||
|
|
||||||
|
static u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address);
|
||||||
|
static void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value);
|
||||||
|
|
||||||
|
/* Power management. */
|
||||||
|
static void SleepSystem();
|
||||||
|
static NORETURN void StopSystem(void *arg = nullptr);
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Secure Memory. */
|
||||||
|
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||||
|
static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
|
||||||
|
static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
|
||||||
|
protected:
|
||||||
|
template<typename F>
|
||||||
|
static ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||||
|
/* Handle the case where the difference is too large to represent. */
|
||||||
|
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate until we get a value in range. */
|
||||||
|
const u64 range_size = ((max + 1) - min);
|
||||||
|
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
||||||
|
while (true) {
|
||||||
|
if (const u64 rnd = f(); rnd < effective_max) {
|
||||||
|
return min + (rnd % range_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
static void CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ namespace ams::kern {
|
|||||||
|
|
||||||
class KTargetSystem {
|
class KTargetSystem {
|
||||||
private:
|
private:
|
||||||
|
friend class KSystemControlBase;
|
||||||
friend class KSystemControl;
|
friend class KSystemControl;
|
||||||
private:
|
private:
|
||||||
static inline constinit bool s_is_debug_mode;
|
static inline constinit bool s_is_debug_mode;
|
||||||
|
|||||||
@@ -405,8 +405,6 @@ namespace ams::kern {
|
|||||||
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
constexpr ThreadState GetState() const { return static_cast<ThreadState>(m_thread_state & ThreadState_Mask); }
|
||||||
constexpr ThreadState GetRawState() const { return m_thread_state; }
|
constexpr ThreadState GetRawState() const { return m_thread_state; }
|
||||||
|
|
||||||
NOINLINE KThreadContext *GetContextForSchedulerLoop();
|
|
||||||
|
|
||||||
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
|
constexpr uintptr_t GetConditionVariableKey() const { return m_condvar_key; }
|
||||||
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
||||||
|
|
||||||
@@ -624,9 +622,7 @@ namespace ams::kern {
|
|||||||
void OnTimer();
|
void OnTimer();
|
||||||
void DoWorkerTaskImpl();
|
void DoWorkerTaskImpl();
|
||||||
public:
|
public:
|
||||||
static constexpr bool IsConditionVariableThreadTreeValid() {
|
static consteval bool IsKThreadStructurallyValid();
|
||||||
return ConditionVariableThreadTreeTraits::IsValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
static KThread *GetThreadFromId(u64 thread_id);
|
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);
|
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;
|
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||||
};
|
};
|
||||||
static_assert(alignof(KThread) == 0x10);
|
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 {
|
class KScopedDisableDispatch {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -39,6 +39,16 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
|
||||||
|
#include <mesosphere/board/qemu/virt/kern_cpu_map.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern::cpu {
|
||||||
|
|
||||||
|
using namespace ams::kern::board::qemu::virt::impl::cpu;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "Unknown board for CPU Map"
|
#error "Unknown board for CPU Map"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace ams::kern {
|
|||||||
private:
|
private:
|
||||||
u32 m_prev_intr_state;
|
u32 m_prev_intr_state;
|
||||||
public:
|
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); }
|
ALWAYS_INLINE ~KScopedInterruptDisable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
|||||||
private:
|
private:
|
||||||
u32 m_prev_intr_state;
|
u32 m_prev_intr_state;
|
||||||
public:
|
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); }
|
ALWAYS_INLINE ~KScopedInterruptEnable() { KInterruptManager::RestoreInterrupts(m_prev_intr_state); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_system_control_base.hpp>
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
||||||
#include <mesosphere/board/nintendo/nx/kern_k_system_control.hpp>
|
#include <mesosphere/board/nintendo/nx/kern_k_system_control.hpp>
|
||||||
@@ -23,6 +24,28 @@
|
|||||||
using ams::kern::board::nintendo::nx::KSystemControl;
|
using ams::kern::board::nintendo::nx::KSystemControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
|
||||||
|
#include <mesosphere/board/qemu/virt/kern_k_system_control.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
using ams::kern::board::qemu::virt::KSystemControl;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "Unknown board for KSystemControl"
|
#error "Unknown board for KSystemControl"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
ALWAYS_INLINE u32 KSystemControlBase::ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
|
||||||
|
u32 v;
|
||||||
|
KSystemControl::ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE void KSystemControlBase::WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
|
||||||
|
u32 v;
|
||||||
|
KSystemControl::ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace ams::kern::arch::arm64::cpu {
|
|||||||
} else {
|
} else {
|
||||||
m_counter = cpu::GetPerformanceCounter(m_which);
|
m_counter = cpu::GetPerformanceCounter(m_which);
|
||||||
}
|
}
|
||||||
DataMemoryBarrier();
|
DataMemoryBarrierInnerShareable();
|
||||||
m_done = true;
|
m_done = true;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,139 +61,3 @@ _ZN3ams4kern4arch5arm643cpu23SynchronizeAllCoresImplEPii:
|
|||||||
5:
|
5:
|
||||||
stlr wzr, [x0]
|
stlr wzr, [x0]
|
||||||
ret
|
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())) {
|
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
|
||||||
GetCurrentThread().ClearSingleStep();
|
GetCurrentThread().ClearSingleStep();
|
||||||
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
|
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
|
||||||
cpu::EnsureInstructionConsistency();
|
cpu::InstructionMemoryBarrier();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
if (user_mode) {
|
if (user_mode) {
|
||||||
KThread *cur_thread = GetCurrentThreadPointer();
|
KThread *cur_thread = GetCurrentThreadPointer();
|
||||||
if (cur_thread->IsTerminationRequested()) {
|
if (cur_thread->IsTerminationRequested()) {
|
||||||
KScopedInterruptEnable ei;
|
EnableInterrupts();
|
||||||
cur_thread->Exit();
|
cur_thread->Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,13 +212,14 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
|
return this->BindGlobal(handler, irq, core_id, priority, manual_clear, level);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->BindLocal(handler, irq, priority, manual_clear);
|
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());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
|
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->UnbindGlobal(irq);
|
return this->UnbindGlobal(irq);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->UnbindLocal(irq);
|
return this->UnbindLocal(irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,13 +248,15 @@ namespace ams::kern::arch::arm64 {
|
|||||||
|
|
||||||
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
R_UNLESS(KInterruptController::IsGlobal(irq) || KInterruptController::IsLocal(irq), svc::ResultOutOfRange());
|
||||||
|
|
||||||
KScopedInterruptDisable di;
|
|
||||||
|
|
||||||
if (KInterruptController::IsGlobal(irq)) {
|
if (KInterruptController::IsGlobal(irq)) {
|
||||||
|
KScopedInterruptDisable di;
|
||||||
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
KScopedSpinLock lk(this->GetGlobalInterruptLock());
|
||||||
return this->ClearGlobal(irq);
|
return this->ClearGlobal(irq);
|
||||||
} else {
|
} else {
|
||||||
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
MESOSPHERE_ASSERT(core_id == GetCurrentCoreId());
|
||||||
|
|
||||||
|
KScopedInterruptDisable di;
|
||||||
return this->ClearLocal(irq);
|
return this->ClearLocal(irq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,10 +169,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
||||||
|
|
||||||
/* Allocate a page for ttbr. */
|
/* 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 u64 asid_tag = (static_cast<u64>(m_asid) << 48ul);
|
||||||
const KVirtualAddress page = m_manager->Allocate();
|
const KVirtualAddress page = m_manager->Allocate();
|
||||||
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
|
MESOSPHERE_ASSERT(page != Null<KVirtualAddress>);
|
||||||
cpu::ClearPageToZero(GetVoidPointer(page));
|
|
||||||
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
|
m_ttbr = GetInteger(KPageTableBase::GetLinearMappedPhysicalAddress(page)) | asid_tag;
|
||||||
|
|
||||||
/* Initialize the base page table. */
|
/* 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());
|
auto sw_reserved_bits = PageTableEntry::EncodeSoftwareReservedBits(head_entry->IsHeadMergeDisabled(), head_entry->IsHeadAndBodyMergeDisabled(), tail_entry->IsTailMergeDisabled());
|
||||||
|
|
||||||
/* Merge! */
|
/* 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);
|
*l1_entry = L1PageTableEntry(PageTableEntry::BlockTag{}, phys_addr, PageTableEntry(entry_template), sw_reserved_bits, false);
|
||||||
|
|
||||||
/* Note that we updated. */
|
/* Note that we updated. */
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ _ZN3ams4kern3svc25CallReturnFromException64Ev:
|
|||||||
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||||
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||||
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
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)]
|
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||||
|
|
||||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||||
|
|||||||
@@ -50,10 +50,6 @@ _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev:
|
|||||||
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
|
.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev
|
||||||
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
|
.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function
|
||||||
_ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
_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. */
|
/* Allocate space for the light ipc data. */
|
||||||
sub sp, sp, #(4 * 8)
|
sub sp, sp, #(4 * 8)
|
||||||
|
|
||||||
@@ -78,13 +74,8 @@ _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev:
|
|||||||
/* Free the stack space for the light ipc data. */
|
/* Free the stack space for the light ipc data. */
|
||||||
add sp, sp, #(4 * 8)
|
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
|
ret
|
||||||
|
|
||||||
|
|
||||||
/* ams::kern::svc::CallReplyAndReceiveLight64() */
|
/* ams::kern::svc::CallReplyAndReceiveLight64() */
|
||||||
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
|
.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits
|
||||||
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
|
.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev
|
||||||
@@ -121,10 +112,6 @@ _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev:
|
|||||||
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
|
.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev
|
||||||
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
|
.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function
|
||||||
_ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
_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. */
|
/* Allocate space for the light ipc data. */
|
||||||
sub sp, sp, #(4 * 8)
|
sub sp, sp, #(4 * 8)
|
||||||
|
|
||||||
@@ -149,8 +136,4 @@ _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev:
|
|||||||
/* Free the stack space for the light ipc data. */
|
/* Free the stack space for the light ipc data. */
|
||||||
add sp, sp, #(4 * 8)
|
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
|
ret
|
||||||
|
|||||||
@@ -656,9 +656,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
||||||
Kernel::GetSystemPageTableManager().Open(table_virt_addr, 1);
|
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. */
|
/* NOTE: Nintendo does not check the result of StoreDataCache. */
|
||||||
cpu::ClearPageToZero(GetVoidPointer(table_virt_addr));
|
|
||||||
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
|
cpu::StoreDataCache(GetVoidPointer(table_virt_addr), PageDirectorySize);
|
||||||
g_reserved_table_phys_addr = table_phys_addr;
|
g_reserved_table_phys_addr = table_phys_addr;
|
||||||
|
|
||||||
@@ -1130,7 +1129,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
size_t cur_size;
|
size_t cur_size;
|
||||||
{
|
{
|
||||||
/* Get the current contiguous range. */
|
/* Get the current contiguous range. */
|
||||||
KPageTableBase::MemoryRange contig_range = {};
|
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
||||||
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
|
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
|
||||||
|
|
||||||
/* Ensure we close the range when we're done. */
|
/* Ensure we close the range when we're done. */
|
||||||
@@ -1288,7 +1287,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
|
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
|
||||||
KPageTableBase::MemoryRange contig_range = {};
|
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
||||||
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
|
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
void PowerOnCpu(int core_id, KPhysicalAddress entry_phys_addr, u64 context_id) {
|
void PowerOnCpu(int core_id, KPhysicalAddress entry_phys_addr, u64 context_id) {
|
||||||
/* Request the secure monitor power on the core. */
|
/* Request the secure monitor power on the core. */
|
||||||
smc::CpuOn(cpu::MultiprocessorAffinityRegisterAccessor().GetCpuOnArgument() | core_id, GetInteger(entry_phys_addr), context_id);
|
::ams::kern::arch::arm64::smc::CpuOn<smc::SmcId_Supervisor, true>(cpu::MultiprocessorAffinityRegisterAccessor().GetCpuOnArgument() | core_id, GetInteger(entry_phys_addr), context_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitOtherCpuPowerOff() {
|
void WaitOtherCpuPowerOff() {
|
||||||
@@ -341,7 +341,9 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
/* Restore pmu registers. */
|
/* Restore pmu registers. */
|
||||||
cpu::SetPmUserEnrEl0(0);
|
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::SetPmOvsClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||||
cpu::SetPmIntEnClrEl1(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())));
|
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uintptr_t DramPhysicalAddress = 0x80000000;
|
constexpr size_t SecureAlignment = 128_KB;
|
||||||
constexpr size_t SecureAlignment = 128_KB;
|
|
||||||
|
|
||||||
/* Global variables for panic. */
|
/* Global variables for panic. */
|
||||||
constinit bool g_call_smc_on_panic;
|
constinit bool g_call_smc_on_panic;
|
||||||
@@ -38,22 +37,6 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
constinit KPhysicalAddress g_secure_region_phys_addr = Null<KPhysicalAddress>;
|
constinit KPhysicalAddress g_secure_region_phys_addr = Null<KPhysicalAddress>;
|
||||||
constinit size_t g_secure_region_size = 0;
|
constinit size_t g_secure_region_size = 0;
|
||||||
|
|
||||||
/* Global variables for randomness. */
|
|
||||||
/* Nintendo uses std::mt19937_t for randomness. */
|
|
||||||
/* To save space (and because mt19337_t isn't secure anyway), */
|
|
||||||
/* We will use TinyMT. */
|
|
||||||
constinit bool g_initialized_random_generator;
|
|
||||||
constinit util::TinyMT g_random_generator{util::ConstantInitialize};
|
|
||||||
constinit KSpinLock g_random_lock;
|
|
||||||
|
|
||||||
ALWAYS_INLINE size_t GetRealMemorySizeForInit() {
|
|
||||||
/* TODO: Move this into a header for the MC in general. */
|
|
||||||
constexpr u32 MemoryControllerConfigurationRegister = 0x70019050;
|
|
||||||
u32 config_value;
|
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0));
|
|
||||||
return static_cast<size_t>(config_value & 0x3FFF) << 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE util::BitPack32 GetKernelConfigurationForInit() {
|
ALWAYS_INLINE util::BitPack32 GetKernelConfigurationForInit() {
|
||||||
u64 value = 0;
|
u64 value = 0;
|
||||||
smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration);
|
smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration);
|
||||||
@@ -86,7 +69,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
|
|
||||||
ALWAYS_INLINE u64 GenerateRandomU64ForInit() {
|
ALWAYS_INLINE u64 GenerateRandomU64ForInit() {
|
||||||
u64 value;
|
u64 value;
|
||||||
smc::init::GenerateRandomBytes(&value, sizeof(value));
|
smc::init::GenerateRandomBytes(std::addressof(value), sizeof(value));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,27 +79,6 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() {
|
|
||||||
return g_random_generator.GenerateRandomU64();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename F>
|
|
||||||
ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
|
||||||
/* Handle the case where the difference is too large to represent. */
|
|
||||||
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate until we get a value in range. */
|
|
||||||
const u64 range_size = ((max + 1) - min);
|
|
||||||
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
|
||||||
while (true) {
|
|
||||||
if (const u64 rnd = f(); rnd < effective_max) {
|
|
||||||
return min + (rnd % range_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) {
|
ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) {
|
||||||
u64 value;
|
u64 value;
|
||||||
smc::GetConfig(&value, 1, which);
|
smc::GetConfig(&value, 1, which);
|
||||||
@@ -324,6 +286,14 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initialization. */
|
/* Initialization. */
|
||||||
|
size_t KSystemControl::Init::GetRealMemorySize() {
|
||||||
|
/* TODO: Move this into a header for the MC in general. */
|
||||||
|
constexpr u32 MemoryControllerConfigurationRegister = 0x70019050;
|
||||||
|
u32 config_value;
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0));
|
||||||
|
return static_cast<size_t>(config_value & 0x3FFF) << 20;
|
||||||
|
}
|
||||||
|
|
||||||
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||||
switch (GetKernelConfigurationForInit().Get<smc::KernelConfiguration::MemorySize>()) {
|
switch (GetKernelConfigurationForInit().Get<smc::KernelConfiguration::MemorySize>()) {
|
||||||
case smc::MemorySize_4GB:
|
case smc::MemorySize_4GB:
|
||||||
@@ -336,23 +306,6 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(uintptr_t base_address) {
|
|
||||||
const size_t real_dram_size = GetRealMemorySizeForInit();
|
|
||||||
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
|
|
||||||
if (intended_dram_size * 2 < real_dram_size) {
|
|
||||||
return base_address;
|
|
||||||
} else {
|
|
||||||
return base_address + ((real_dram_size - intended_dram_size) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSystemControl::Init::GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out) {
|
|
||||||
*out = {
|
|
||||||
.address = GetInteger(GetKernelPhysicalBaseAddress(DramPhysicalAddress)) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax,
|
|
||||||
._08 = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
||||||
return GetKernelConfigurationForInit().Get<smc::KernelConfiguration::IncreaseThreadResourceLimit>();
|
return GetKernelConfigurationForInit().Get<smc::KernelConfiguration::IncreaseThreadResourceLimit>();
|
||||||
}
|
}
|
||||||
@@ -424,17 +377,17 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||||
smc::init::CpuOn(core_id, entrypoint, arg);
|
MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<smc::SmcId_Supervisor, false>(core_id, entrypoint, arg)) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Randomness for Initialization. */
|
/* Randomness for Initialization. */
|
||||||
void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) {
|
void KSystemControl::Init::GenerateRandom(u64 *dst, size_t count) {
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38);
|
MESOSPHERE_INIT_ABORT_UNLESS(count <= 7);
|
||||||
smc::init::GenerateRandomBytes(dst, size);
|
smc::init::GenerateRandomBytes(dst, count * sizeof(u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
|
u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
|
||||||
return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
|
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64ForInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* System Initialization. */
|
/* System Initialization. */
|
||||||
@@ -443,8 +396,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
{
|
{
|
||||||
u64 seed;
|
u64 seed;
|
||||||
smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed));
|
smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed));
|
||||||
g_random_generator.Initialize(reinterpret_cast<u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||||
g_initialized_random_generator = true;
|
s_initialized_random_generator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set IsDebugMode. */
|
/* Set IsDebugMode. */
|
||||||
@@ -483,25 +436,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize());
|
smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* System ResourceLimit initialization. */
|
/* Initialize the system resource limit (and potentially other things). */
|
||||||
{
|
KSystemControlBase::InitializePhase1(true);
|
||||||
/* Construct the resource limit object. */
|
|
||||||
KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit();
|
|
||||||
KAutoObject::Create<KResourceLimit>(std::addressof(sys_res_limit));
|
|
||||||
sys_res_limit.Initialize();
|
|
||||||
|
|
||||||
/* Set the initial limits. */
|
|
||||||
const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
|
|
||||||
const auto &slab_counts = init::GetSlabResourceCounts();
|
|
||||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
|
|
||||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
|
|
||||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent));
|
|
||||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory));
|
|
||||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession));
|
|
||||||
|
|
||||||
/* Reserve system memory. */
|
|
||||||
MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KSystemControl::InitializePhase2() {
|
void KSystemControl::InitializePhase2() {
|
||||||
@@ -520,11 +456,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
g_secure_applet_memory_address = KMemoryLayout::GetLinearVirtualAddress(secure_applet_memory_phys_addr);
|
g_secure_applet_memory_address = KMemoryLayout::GetLinearVirtualAddress(secure_applet_memory_phys_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize KTrace. */
|
/* Initialize KTrace (and potentially other init). */
|
||||||
if constexpr (IsKTraceEnabled) {
|
KSystemControlBase::InitializePhase2();
|
||||||
const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion();
|
|
||||||
KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 KSystemControl::GetCreateProcessMemoryPool() {
|
u32 KSystemControl::GetCreateProcessMemoryPool() {
|
||||||
@@ -546,29 +479,29 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Randomness. */
|
/* Randomness. */
|
||||||
void KSystemControl::GenerateRandomBytes(void *dst, size_t size) {
|
void KSystemControl::GenerateRandom(u64 *dst, size_t count) {
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38);
|
MESOSPHERE_INIT_ABORT_UNLESS(count <= 7);
|
||||||
smc::GenerateRandomBytes(dst, size);
|
smc::GenerateRandomBytes(dst, count * sizeof(u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
||||||
KScopedInterruptDisable intr_disable;
|
KScopedInterruptDisable intr_disable;
|
||||||
KScopedSpinLock lk(g_random_lock);
|
KScopedSpinLock lk(s_random_lock);
|
||||||
|
|
||||||
|
|
||||||
if (AMS_LIKELY(g_initialized_random_generator)) {
|
if (AMS_LIKELY(s_initialized_random_generator)) {
|
||||||
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
|
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||||
} else {
|
} else {
|
||||||
return GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
|
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KSystemControl::GenerateRandomU64() {
|
u64 KSystemControl::GenerateRandomU64() {
|
||||||
KScopedInterruptDisable intr_disable;
|
KScopedInterruptDisable intr_disable;
|
||||||
KScopedSpinLock lk(g_random_lock);
|
KScopedSpinLock lk(s_random_lock);
|
||||||
|
|
||||||
if (AMS_LIKELY(g_initialized_random_generator)) {
|
if (AMS_LIKELY(s_initialized_random_generator)) {
|
||||||
return GenerateRandomU64FromGenerator();
|
return s_random_generator.GenerateRandomU64();
|
||||||
} else {
|
} else {
|
||||||
return GenerateRandomU64FromSmc();
|
return GenerateRandomU64FromSmc();
|
||||||
}
|
}
|
||||||
@@ -672,52 +605,18 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* User access. */
|
/* User access. */
|
||||||
void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
void KSystemControl::CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
/* Get the function id for the current call. */
|
|
||||||
u64 function_id = args->r[0];
|
|
||||||
|
|
||||||
/* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */
|
|
||||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
|
||||||
auto *bim = page_table.GetBlockInfoManager();
|
|
||||||
|
|
||||||
constexpr size_t MaxMappedRegisters = 7;
|
|
||||||
std::array<KPageGroup, MaxMappedRegisters> page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), };
|
|
||||||
|
|
||||||
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
|
||||||
const size_t reg_id = i + 1;
|
|
||||||
if (function_id & (1ul << (8 + reg_id))) {
|
|
||||||
/* Create and open a new page group for the address. */
|
|
||||||
KVirtualAddress virt_addr = args->r[reg_id];
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) {
|
|
||||||
/* Translate the virtual address to a physical address. */
|
|
||||||
const auto it = page_groups[i].begin();
|
|
||||||
MESOSPHERE_ASSERT(it != page_groups[i].end());
|
|
||||||
MESOSPHERE_ASSERT(it->GetNumPages() == 1);
|
|
||||||
|
|
||||||
args->r[reg_id] = GetInteger(it->GetAddress()) | (GetInteger(virt_addr) & (PageSize - 1));
|
|
||||||
} else {
|
|
||||||
/* If we couldn't map, we should clear the address. */
|
|
||||||
args->r[reg_id] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invoke the secure monitor. */
|
/* Invoke the secure monitor. */
|
||||||
smc::CallSecureMonitorFromUser(args);
|
return smc::CallSecureMonitorFromUser(args);
|
||||||
|
|
||||||
/* Make sure that we close any pages that we opened. */
|
|
||||||
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
|
||||||
page_groups[i].Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Secure Memory. */
|
/* Secure Memory. */
|
||||||
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||||
if (pool == KMemoryManager::Pool_Applet) {
|
if (pool == KMemoryManager::Pool_Applet) {
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
|
||||||
}
|
}
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
|
Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
|
||||||
|
|||||||
@@ -20,18 +20,27 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct SecureMonitorArguments {
|
|
||||||
u64 x[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UserFunctionId : u32 {
|
enum UserFunctionId : u32 {
|
||||||
UserFunctionId_SetConfig = 0xC3000401,
|
UserFunctionId_SetConfig = 0xC3000401,
|
||||||
|
UserFunctionId_GetConfigUser = 0xC3000002,
|
||||||
|
UserFunctionId_GetResult = 0xC3000003,
|
||||||
|
UserFunctionId_GetResultData = 0xC3000404,
|
||||||
|
UserFunctionId_ModularExponentiate = 0xC3000E05,
|
||||||
|
UserFunctionId_GenerateRandomBytes = 0xC3000006,
|
||||||
|
UserFunctionId_GenerateAesKek = 0xC3000007,
|
||||||
|
UserFunctionId_LoadAesKey = 0xC3000008,
|
||||||
|
UserFunctionId_ComputeAes = 0xC3000009,
|
||||||
|
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
|
||||||
|
UserFunctionId_ComputeCmac = 0xC300040B,
|
||||||
|
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
|
||||||
|
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
|
||||||
|
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
|
||||||
|
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
|
||||||
|
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
|
||||||
|
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FunctionId : u32 {
|
enum FunctionId : u32 {
|
||||||
FunctionId_CpuSuspend = 0xC4000001,
|
|
||||||
FunctionId_CpuOff = 0x84000002,
|
|
||||||
FunctionId_CpuOn = 0xC4000003,
|
|
||||||
FunctionId_GetConfig = 0xC3000004,
|
FunctionId_GetConfig = 0xC3000004,
|
||||||
FunctionId_GenerateRandomBytes = 0xC3000005,
|
FunctionId_GenerateRandomBytes = 0xC3000005,
|
||||||
FunctionId_Panic = 0xC3000006,
|
FunctionId_Panic = 0xC3000006,
|
||||||
@@ -42,171 +51,60 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
FunctionId_SetConfig = 0xC3000409,
|
FunctionId_SetConfig = 0xC3000409,
|
||||||
};
|
};
|
||||||
|
|
||||||
void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) {
|
|
||||||
/* Load arguments into registers. */
|
|
||||||
register u64 x0 asm("x0") = args.x[0];
|
|
||||||
register u64 x1 asm("x1") = args.x[1];
|
|
||||||
register u64 x2 asm("x2") = args.x[2];
|
|
||||||
register u64 x3 asm("x3") = args.x[3];
|
|
||||||
register u64 x4 asm("x4") = args.x[4];
|
|
||||||
register u64 x5 asm("x5") = args.x[5];
|
|
||||||
register u64 x6 asm("x6") = args.x[6];
|
|
||||||
register u64 x7 asm("x7") = args.x[7];
|
|
||||||
|
|
||||||
/* Actually make the call. */
|
|
||||||
{
|
|
||||||
/* Disable interrupts while making the call. */
|
|
||||||
KScopedInterruptDisable intr_disable;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Backup the current thread pointer. */
|
|
||||||
const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
|
|
||||||
|
|
||||||
__asm__ __volatile__("smc #1"
|
|
||||||
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
|
||||||
:
|
|
||||||
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Restore the current thread pointer into X18. */
|
|
||||||
cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
|
|
||||||
|
|
||||||
/* Store arguments to output. */
|
|
||||||
args.x[0] = x0;
|
|
||||||
args.x[1] = x1;
|
|
||||||
args.x[2] = x2;
|
|
||||||
args.x[3] = x3;
|
|
||||||
args.x[4] = x4;
|
|
||||||
args.x[5] = x5;
|
|
||||||
args.x[6] = x6;
|
|
||||||
args.x[7] = x7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CallUserSecureMonitorFunction(ams::svc::lp64::SecureMonitorArguments *args) {
|
|
||||||
/* Load arguments into registers. */
|
|
||||||
register u64 x0 asm("x0") = args->r[0];
|
|
||||||
register u64 x1 asm("x1") = args->r[1];
|
|
||||||
register u64 x2 asm("x2") = args->r[2];
|
|
||||||
register u64 x3 asm("x3") = args->r[3];
|
|
||||||
register u64 x4 asm("x4") = args->r[4];
|
|
||||||
register u64 x5 asm("x5") = args->r[5];
|
|
||||||
register u64 x6 asm("x6") = args->r[6];
|
|
||||||
register u64 x7 asm("x7") = args->r[7];
|
|
||||||
|
|
||||||
/* Actually make the call. */
|
|
||||||
{
|
|
||||||
/* Disable interrupts while making the call. */
|
|
||||||
KScopedInterruptDisable intr_disable;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Backup the current thread pointer. */
|
|
||||||
const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
|
|
||||||
|
|
||||||
__asm__ __volatile__("smc #0"
|
|
||||||
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
|
||||||
:
|
|
||||||
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Restore the current thread pointer into X18. */
|
|
||||||
cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
|
|
||||||
|
|
||||||
/* Store arguments to output. */
|
|
||||||
args->r[0] = x0;
|
|
||||||
args->r[1] = x1;
|
|
||||||
args->r[2] = x2;
|
|
||||||
args->r[3] = x3;
|
|
||||||
args->r[4] = x4;
|
|
||||||
args->r[5] = x5;
|
|
||||||
args->r[6] = x6;
|
|
||||||
args->r[7] = x7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
|
|
||||||
/* Load arguments into registers. */
|
|
||||||
register u64 x0 asm("x0") = args.x[0];
|
|
||||||
register u64 x1 asm("x1") = args.x[1];
|
|
||||||
register u64 x2 asm("x2") = args.x[2];
|
|
||||||
register u64 x3 asm("x3") = args.x[3];
|
|
||||||
register u64 x4 asm("x4") = args.x[4];
|
|
||||||
register u64 x5 asm("x5") = args.x[5];
|
|
||||||
register u64 x6 asm("x6") = args.x[6];
|
|
||||||
register u64 x7 asm("x7") = args.x[7];
|
|
||||||
|
|
||||||
/* Actually make the call. */
|
|
||||||
__asm__ __volatile__("smc #1"
|
|
||||||
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
|
||||||
:
|
|
||||||
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Store arguments to output. */
|
|
||||||
args.x[0] = x0;
|
|
||||||
args.x[1] = x1;
|
|
||||||
args.x[2] = x2;
|
|
||||||
args.x[3] = x3;
|
|
||||||
args.x[4] = x4;
|
|
||||||
args.x[5] = x5;
|
|
||||||
args.x[6] = x6;
|
|
||||||
args.x[7] = x7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Global lock for generate random bytes. */
|
/* Global lock for generate random bytes. */
|
||||||
KSpinLock g_generate_random_lock;
|
constinit KSpinLock g_generate_random_lock;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SMC functionality needed for init. */
|
/* SMC functionality needed for init. */
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
|
||||||
SecureMonitorArguments args = { FunctionId_CpuOn, core_id, entrypoint, arg };
|
|
||||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
|
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
|
||||||
SecureMonitorArguments args = { FunctionId_GetConfig, static_cast<u32>(config_item) };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GetConfig, static_cast<u32>(config_item) } };
|
||||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, false>(args.r);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.r[0]) == SmcResult::Success));
|
||||||
|
|
||||||
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
||||||
out[i] = args.x[1 + i];
|
out[i] = args.r[1 + i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRandomBytes(void *dst, size_t size) {
|
void GenerateRandomBytes(void *dst, size_t size) {
|
||||||
/* Call SmcGenerateRandomBytes() */
|
/* Call SmcGenerateRandomBytes() */
|
||||||
SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GenerateRandomBytes, size } };
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0]));
|
MESOSPHERE_INIT_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.r[0]));
|
||||||
|
|
||||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, false>(args.r);
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.r[0]) == SmcResult::Success));
|
||||||
|
|
||||||
/* Copy output. */
|
/* Copy output. */
|
||||||
std::memcpy(dst, std::addressof(args.x[1]), size);
|
std::memcpy(dst, std::addressof(args.r[1]), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) {
|
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) {
|
||||||
SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ReadWriteRegister, address, mask, value } };
|
||||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
|
||||||
*out = args.x[1];
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, false>(args.r);
|
||||||
return static_cast<SmcResult>(args.x[0]) == SmcResult::Success;
|
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.r[0]) == SmcResult::Success));
|
||||||
|
|
||||||
|
*out = args.r[1];
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
|
bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
|
||||||
SecureMonitorArguments args = { FunctionId_GetConfig, static_cast<u32>(config_item) };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GetConfig, static_cast<u32>(config_item) } };
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
if (static_cast<SmcResult>(args.x[0]) != SmcResult::Success) {
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
|
if (AMS_UNLIKELY(static_cast<SmcResult>(args.r[0]) != SmcResult::Success)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
||||||
out[i] = args.x[1 + i];
|
out[i] = args.r[1 + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -217,55 +115,58 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SetConfig(ConfigItem config_item, u64 value) {
|
bool SetConfig(ConfigItem config_item, u64 value) {
|
||||||
SecureMonitorArguments args = { FunctionId_SetConfig, static_cast<u32>(config_item), 0, value };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_SetConfig, static_cast<u32>(config_item), 0, value } };
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
return static_cast<SmcResult>(args.x[0]) == SmcResult::Success;
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
|
bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
|
||||||
SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ReadWriteRegister, address, mask, value } };
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
*out = static_cast<u32>(args.x[1]);
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
return static_cast<SmcResult>(args.x[0]) == SmcResult::Success;
|
|
||||||
|
*out = static_cast<u32>(args.r[1]);
|
||||||
|
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureCarveout(size_t which, uintptr_t address, size_t size) {
|
void ConfigureCarveout(size_t which, uintptr_t address, size_t size) {
|
||||||
SecureMonitorArguments args = { FunctionId_ConfigureCarveout, static_cast<u64>(which), static_cast<u64>(address), static_cast<u64>(size) };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ConfigureCarveout, static_cast<u64>(which), static_cast<u64>(address), static_cast<u64>(size) } };
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
SecureMonitorArguments args = { FunctionId_CpuOn, core_id, static_cast<u64>(entrypoint), static_cast<u64>(arg) };
|
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.r[0]) == SmcResult::Success));
|
||||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRandomBytes(void *dst, size_t size) {
|
void GenerateRandomBytes(void *dst, size_t size) {
|
||||||
/* Setup for call. */
|
/* Setup for call. */
|
||||||
SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GenerateRandomBytes, size } };
|
||||||
MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0]));
|
MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.r[0]));
|
||||||
|
|
||||||
/* Make call. */
|
/* Make call. */
|
||||||
{
|
{
|
||||||
KScopedInterruptDisable intr_disable;
|
KScopedInterruptDisable intr_disable;
|
||||||
KScopedSpinLock lk(g_generate_random_lock);
|
KScopedSpinLock lk(g_generate_random_lock);
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
}
|
}
|
||||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.r[0]) == SmcResult::Success));
|
||||||
|
|
||||||
/* Copy output. */
|
/* Copy output. */
|
||||||
std::memcpy(dst, std::addressof(args.x[1]), size);
|
std::memcpy(dst, std::addressof(args.r[1]), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NORETURN Panic(u32 color) {
|
void NORETURN Panic(u32 color) {
|
||||||
SecureMonitorArguments args = { FunctionId_Panic, color };
|
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_Panic, color } };
|
||||||
CallPrivilegedSecureMonitorFunction(args);
|
|
||||||
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor, true>(args.r);
|
||||||
|
|
||||||
AMS_INFINITE_LOOP();
|
AMS_INFINITE_LOOP();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
CallUserSecureMonitorFunction(args);
|
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_User, true>(args->r);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,10 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere.hpp>
|
#include <mesosphere.hpp>
|
||||||
|
#include <mesosphere/arch/arm64/kern_secure_monitor_base.hpp>
|
||||||
|
|
||||||
namespace ams::kern::board::nintendo::nx::smc {
|
namespace ams::kern::board::nintendo::nx::smc {
|
||||||
|
|
||||||
/* Types. */
|
/* Types. */
|
||||||
|
enum SmcId {
|
||||||
|
SmcId_User = 0,
|
||||||
|
SmcId_Supervisor = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum MemorySize {
|
enum MemorySize {
|
||||||
MemorySize_4GB = 0,
|
MemorySize_4GB = 0,
|
||||||
MemorySize_6GB = 1,
|
MemorySize_6GB = 1,
|
||||||
@@ -105,15 +111,12 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
|
|
||||||
bool SetConfig(ConfigItem config_item, u64 value);
|
bool SetConfig(ConfigItem config_item, u64 value);
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
|
||||||
|
|
||||||
void NORETURN Panic(u32 color);
|
void NORETURN Panic(u32 color);
|
||||||
|
|
||||||
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
|
||||||
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
|
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
|
||||||
void GenerateRandomBytes(void *dst, size_t size);
|
void GenerateRandomBytes(void *dst, size_t size);
|
||||||
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value);
|
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value);
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
#include "kern_secure_monitor.hpp"
|
||||||
|
|
||||||
|
namespace ams::kern::board::qemu::virt {
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
/* Invoke the secure monitor. */
|
||||||
|
return smc::CallSecureMonitorFromUser(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
#include "kern_secure_monitor.hpp"
|
||||||
|
|
||||||
|
namespace ams::kern::board::qemu::virt::smc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum UserFunctionId : u32 {
|
||||||
|
UserFunctionId_SetConfig = 0xC3000401,
|
||||||
|
UserFunctionId_GetConfig = 0xC3000002,
|
||||||
|
UserFunctionId_GetResult = 0xC3000003,
|
||||||
|
UserFunctionId_GetResultData = 0xC3000404,
|
||||||
|
UserFunctionId_ModularExponentiate = 0xC3000E05,
|
||||||
|
UserFunctionId_GenerateRandomBytes = 0xC3000006,
|
||||||
|
UserFunctionId_GenerateAesKek = 0xC3000007,
|
||||||
|
UserFunctionId_LoadAesKey = 0xC3000008,
|
||||||
|
UserFunctionId_ComputeAes = 0xC3000009,
|
||||||
|
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
|
||||||
|
UserFunctionId_ComputeCmac = 0xC300040B,
|
||||||
|
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
|
||||||
|
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
|
||||||
|
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
|
||||||
|
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
|
||||||
|
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
|
||||||
|
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
MESOSPHERE_LOG("Received SMC [%p %p %p %p %p %p %p %p] from %s\n", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]), GetCurrentProcess().GetName());
|
||||||
|
|
||||||
|
switch (args->r[0]) {
|
||||||
|
case UserFunctionId_GetConfig:
|
||||||
|
{
|
||||||
|
switch (static_cast<ConfigItem>(args->r[1])) {
|
||||||
|
case ConfigItem::ExosphereApiVersion:
|
||||||
|
args->r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
|
||||||
|
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
|
||||||
|
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
|
||||||
|
(static_cast<u64>(13) << 32) |
|
||||||
|
(static_cast<u64>(GetTargetFirmware()) << 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MESOSPHERE_PANIC("Unhandled GetConfig\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
args->r[0] = static_cast<u64>(SmcResult::Success);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MESOSPHERE_PANIC("Unhandled SMC [%p %p %p %p %p %p %p %p]", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern::board::qemu::virt::smc {
|
||||||
|
|
||||||
|
enum class ConfigItem : u32 {
|
||||||
|
/* Standard config items. */
|
||||||
|
DisableProgramVerification = 1,
|
||||||
|
DramId = 2,
|
||||||
|
SecurityEngineIrqNumber = 3,
|
||||||
|
Version = 4,
|
||||||
|
HardwareType = 5,
|
||||||
|
IsRetail = 6,
|
||||||
|
IsRecoveryBoot = 7,
|
||||||
|
DeviceId = 8,
|
||||||
|
BootReason = 9,
|
||||||
|
MemoryMode = 10,
|
||||||
|
IsDebugMode = 11,
|
||||||
|
KernelConfiguration = 12,
|
||||||
|
IsChargerHiZModeEnabled = 13,
|
||||||
|
IsQuest = 14,
|
||||||
|
RegulatorType = 15,
|
||||||
|
DeviceUniqueKeyGeneration = 16,
|
||||||
|
Package2Hash = 17,
|
||||||
|
|
||||||
|
/* Extension config items for exosphere. */
|
||||||
|
ExosphereApiVersion = 65000,
|
||||||
|
ExosphereNeedsReboot = 65001,
|
||||||
|
ExosphereNeedsShutdown = 65002,
|
||||||
|
ExosphereGitCommitHash = 65003,
|
||||||
|
ExosphereHasRcmBugPatch = 65004,
|
||||||
|
ExosphereBlankProdInfo = 65005,
|
||||||
|
ExosphereAllowCalWrites = 65006,
|
||||||
|
ExosphereEmummcType = 65007,
|
||||||
|
ExospherePayloadAddress = 65008,
|
||||||
|
ExosphereLogConfiguration = 65009,
|
||||||
|
ExosphereForceEnableUsb30 = 65010,
|
||||||
|
ExosphereSupportedHosVersion = 65011,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SmcResult {
|
||||||
|
Success = 0,
|
||||||
|
NotImplemented = 1,
|
||||||
|
InvalidArgument = 2,
|
||||||
|
InProgress = 3,
|
||||||
|
NoAsyncOperation = 4,
|
||||||
|
InvalidAsyncOperation = 5,
|
||||||
|
NotPermitted = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,6 +32,9 @@ namespace ams::kern {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
|
||||||
|
KDebugLogImpl::PutStringBySemihosting(str);
|
||||||
|
#else
|
||||||
while (*str) {
|
while (*str) {
|
||||||
/* Get a character. */
|
/* Get a character. */
|
||||||
const char c = *(str++);
|
const char c = *(str++);
|
||||||
@@ -44,6 +47,7 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KDebugLogImpl::Flush();
|
KDebugLogImpl::Flush();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
|
#if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
|
||||||
@@ -54,6 +58,11 @@ namespace ams::kern {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
|
||||||
|
/* TODO: should we do this properly? */
|
||||||
|
KDebugLogImpl::PutStringBySemihosting(user_str.GetUnsafePointer());
|
||||||
|
MESOSPHERE_UNUSED(len);
|
||||||
|
#else
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
/* Get a character. */
|
/* Get a character. */
|
||||||
char c;
|
char c;
|
||||||
@@ -67,6 +76,7 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KDebugLogImpl::Flush();
|
KDebugLogImpl::Flush();
|
||||||
|
#endif
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ams::kern::KDebugLogImpl::PutStringBySemihosting(const char *str) */
|
||||||
|
.section .text._ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, "ax", %progbits
|
||||||
|
.global _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc
|
||||||
|
.type _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, %function
|
||||||
|
.balign 0x10
|
||||||
|
_ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc:
|
||||||
|
mov x1, x0
|
||||||
|
mov x0, #0x4
|
||||||
|
hlt #0xF000
|
||||||
|
ret
|
||||||
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
#include "kern_debug_log_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
|
||||||
|
|
||||||
|
bool KDebugLogImpl::Initialize() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDebugLogImpl::PutChar(char c) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDebugLogImpl::Flush() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDebugLogImpl::Save() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void KDebugLogImpl::Restore() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error "Unknown Debug device!"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ namespace ams::kern {
|
|||||||
class KDebugLogImpl {
|
class KDebugLogImpl {
|
||||||
public:
|
public:
|
||||||
static NOINLINE bool Initialize();
|
static NOINLINE bool Initialize();
|
||||||
|
static NOINLINE void PutStringBySemihosting(const char *s);
|
||||||
static NOINLINE void PutChar(char c);
|
static NOINLINE void PutChar(char c);
|
||||||
static NOINLINE void Flush();
|
static NOINLINE void Flush();
|
||||||
|
|
||||||
|
|||||||
@@ -117,11 +117,15 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result KCapabilities::MapRange(const util::BitPack32 cap, const util::BitPack32 size_cap, KProcessPageTable *page_table) {
|
Result KCapabilities::MapRange(const util::BitPack32 cap, const util::BitPack32 size_cap, KProcessPageTable *page_table) {
|
||||||
|
/* Get/validate address/size */
|
||||||
|
#if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES)
|
||||||
|
const u64 phys_addr = static_cast<u64>(cap.Get<MapRange::Address>() | (size_cap.Get<MapRangeSize::AddressHigh>() << MapRange::Address::Count)) * PageSize;
|
||||||
|
#else
|
||||||
|
const u64 phys_addr = static_cast<u64>(cap.Get<MapRange::Address>()) * PageSize;
|
||||||
|
|
||||||
/* Validate reserved bits are unused. */
|
/* Validate reserved bits are unused. */
|
||||||
R_UNLESS(size_cap.Get<MapRangeSize::Reserved>() == 0, svc::ResultOutOfRange());
|
R_UNLESS(size_cap.Get<MapRangeSize::Reserved>() == 0, svc::ResultOutOfRange());
|
||||||
|
#endif
|
||||||
/* Get/validate address/size */
|
|
||||||
const u64 phys_addr = cap.Get<MapRange::Address>() * PageSize;
|
|
||||||
const size_t num_pages = size_cap.Get<MapRangeSize::Pages>();
|
const size_t num_pages = size_cap.Get<MapRangeSize::Pages>();
|
||||||
const size_t size = num_pages * PageSize;
|
const size_t size = num_pages * PageSize;
|
||||||
R_UNLESS(phys_addr == GetInteger(KPhysicalAddress(phys_addr)), svc::ResultInvalidAddress());
|
R_UNLESS(phys_addr == GetInteger(KPhysicalAddress(phys_addr)), svc::ResultInvalidAddress());
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ namespace ams::kern {
|
|||||||
{
|
{
|
||||||
const u32 has_waiter_flag = 1;
|
const u32 has_waiter_flag = 1;
|
||||||
WriteToUser(key, std::addressof(has_waiter_flag));
|
WriteToUser(key, std::addressof(has_waiter_flag));
|
||||||
cpu::DataMemoryBarrier();
|
cpu::DataMemoryBarrierInnerShareable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the value to userspace. */
|
/* Write the value to userspace. */
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace ams::kern {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uintptr_t DramPhysicalAddress = 0x80000000;
|
|
||||||
constexpr size_t ReservedEarlyDramSize = 0x60000;
|
constexpr size_t ReservedEarlyDramSize = 0x60000;
|
||||||
|
|
||||||
constexpr size_t CarveoutAlignment = 0x20000;
|
constexpr size_t CarveoutAlignment = 0x20000;
|
||||||
@@ -100,7 +99,7 @@ namespace ams::kern {
|
|||||||
|
|
||||||
void SetupDramPhysicalMemoryRegions() {
|
void SetupDramPhysicalMemoryRegions() {
|
||||||
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
|
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||||
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
|
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(ams::kern::MainMemoryAddress);
|
||||||
|
|
||||||
/* Insert blocks into the tree. */
|
/* Insert blocks into the tree. */
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
|
||||||
@@ -173,16 +172,21 @@ namespace ams::kern {
|
|||||||
InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr);
|
InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr);
|
||||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
|
||||||
|
|
||||||
/* Insert the pool management region. */
|
/* Determine final total overhead size. */
|
||||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
|
||||||
const uintptr_t pool_management_start = unsafe_system_pool_start - total_overhead_size;
|
|
||||||
|
/* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the four UserPool regions are contiguous. */
|
||||||
|
|
||||||
|
/* Insert the system pool. */
|
||||||
|
const uintptr_t system_pool_start = pool_partitions_start + total_overhead_size;
|
||||||
|
const size_t system_pool_size = unsafe_system_pool_start - system_pool_start;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(system_pool_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
||||||
|
|
||||||
|
/* Insert the pool management region. */
|
||||||
|
const uintptr_t pool_management_start = pool_partitions_start;
|
||||||
const size_t pool_management_size = total_overhead_size;
|
const size_t pool_management_size = total_overhead_size;
|
||||||
u32 pool_management_attr = 0;
|
u32 pool_management_attr = 0;
|
||||||
InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
|
InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
|
||||||
|
|
||||||
/* Insert the system pool. */
|
|
||||||
const uintptr_t system_pool_size = pool_management_start - pool_partitions_start;
|
|
||||||
InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
|
||||||
} else {
|
} else {
|
||||||
/* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */
|
/* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */
|
||||||
|
|
||||||
@@ -249,14 +253,18 @@ namespace ams::kern {
|
|||||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert the secure pool. */
|
/* Validate the true overhead size. */
|
||||||
InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
|
||||||
|
|
||||||
/* Insert the pool management region. */
|
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= approximate_total_overhead_size);
|
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= approximate_total_overhead_size);
|
||||||
|
|
||||||
const uintptr_t pool_management_start = pool_partitions_start + secure_pool_size;
|
/* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the UserPool regions are contiguous. */
|
||||||
const size_t pool_management_size = unsafe_memory_start - pool_management_start;
|
|
||||||
|
/* Insert the secure pool. */
|
||||||
|
const uintptr_t secure_pool_start = unsafe_memory_start - secure_pool_size;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(secure_pool_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
||||||
|
|
||||||
|
/* Insert the pool management region. */
|
||||||
|
const uintptr_t pool_management_start = pool_partitions_start;
|
||||||
|
const size_t pool_management_size = secure_pool_start - pool_management_start;
|
||||||
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= pool_management_size);
|
MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= pool_management_size);
|
||||||
|
|
||||||
u32 pool_management_attr = 0;
|
u32 pool_management_attr = 0;
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr size_t ReservedEarlyDramSize = 0x00080000;
|
||||||
|
|
||||||
|
template<typename... T> requires (std::same_as<T, KMemoryRegionAttr> && ...)
|
||||||
|
constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) {
|
||||||
|
return util::FromUnderlying<KMemoryRegionType>(util::ToUnderlying(base) | (util::ToUnderlying<T>(attr) | ...));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) {
|
||||||
|
const u32 attr = cur_attr++;
|
||||||
|
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(start, size, phys_type, attr));
|
||||||
|
const KMemoryRegion *phys = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(phys_type, attr);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(phys != nullptr);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(phys->GetEndAddress() != 0);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size, virt_type, attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace init {
|
||||||
|
|
||||||
|
void SetupDevicePhysicalMemoryRegions() {
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08000000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptDistributor, KMemoryRegionAttr_ShouldKernelMap)));
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08010000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptCpuInterface, KMemoryRegionAttr_ShouldKernelMap)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupDramPhysicalMemoryRegions() {
|
||||||
|
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||||
|
const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(ams::kern::MainMemoryAddress);
|
||||||
|
|
||||||
|
/* Insert blocks into the tree. */
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
|
||||||
|
|
||||||
|
/* Insert the KTrace block at the end of Dram, if KTrace is enabled. */
|
||||||
|
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
|
||||||
|
if constexpr (IsKTraceEnabled) {
|
||||||
|
const KPhysicalAddress ktrace_buffer_phys_addr = physical_memory_base_address + intended_memory_size - KTraceBufferSize;
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(ktrace_buffer_phys_addr), KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupPoolPartitionMemoryRegions() {
|
||||||
|
/* Start by identifying the extents of the DRAM memory region. */
|
||||||
|
const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents();
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(dram_extents.GetEndAddress() != 0);
|
||||||
|
|
||||||
|
/* Determine the end of the pool region. */
|
||||||
|
const uintptr_t pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
|
||||||
|
|
||||||
|
/* Find the start of the kernel DRAM region. */
|
||||||
|
const KMemoryRegion *kernel_dram_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramKernelBase);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(kernel_dram_region != nullptr);
|
||||||
|
|
||||||
|
/* Find the start of the pool partitions region. */
|
||||||
|
const KMemoryRegion *pool_partitions_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(KMemoryRegionType_DramPoolPartition, 0);
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS(pool_partitions_region != nullptr);
|
||||||
|
const uintptr_t pool_partitions_start = pool_partitions_region->GetAddress();
|
||||||
|
|
||||||
|
/* Setup the pool partition layouts. */
|
||||||
|
/* Get Application and Applet pool sizes. */
|
||||||
|
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
|
||||||
|
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
|
||||||
|
const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
|
||||||
|
|
||||||
|
/* Decide on starting addresses for our pools. */
|
||||||
|
const uintptr_t application_pool_start = pool_end - application_pool_size;
|
||||||
|
const uintptr_t applet_pool_start = application_pool_start - applet_pool_size;
|
||||||
|
const uintptr_t unsafe_system_pool_start = util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, PageSize);
|
||||||
|
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
|
||||||
|
|
||||||
|
/* We want to arrange application pool depending on where the middle of dram is. */
|
||||||
|
const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
|
||||||
|
u32 cur_pool_attr = 0;
|
||||||
|
size_t total_overhead_size = 0;
|
||||||
|
|
||||||
|
/* Insert the application pool. */
|
||||||
|
if (application_pool_size > 0) {
|
||||||
|
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
|
||||||
|
} else {
|
||||||
|
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
|
||||||
|
const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert the applet pool. */
|
||||||
|
if (applet_pool_size > 0) {
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(applet_pool_start, applet_pool_size, KMemoryRegionType_DramAppletPool, KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert the nonsecure system pool. */
|
||||||
|
if (unsafe_system_pool_size > 0) {
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr);
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine final total overhead size. */
|
||||||
|
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
|
||||||
|
|
||||||
|
/* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the four UserPool regions are contiguous. */
|
||||||
|
|
||||||
|
/* Insert the system pool. */
|
||||||
|
const uintptr_t system_pool_start = pool_partitions_start + total_overhead_size;
|
||||||
|
const size_t system_pool_size = unsafe_system_pool_start - system_pool_start;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(system_pool_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
||||||
|
|
||||||
|
/* Insert the pool management region. */
|
||||||
|
const uintptr_t pool_management_start = pool_partitions_start;
|
||||||
|
const size_t pool_management_size = total_overhead_size;
|
||||||
|
u32 pool_management_attr = 0;
|
||||||
|
InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -238,7 +238,7 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Generate random entropy. */
|
/* Generate random entropy. */
|
||||||
KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy));
|
KSystemControl::GenerateRandom(m_entropy, util::size(m_entropy));
|
||||||
|
|
||||||
/* Clear remaining fields. */
|
/* Clear remaining fields. */
|
||||||
m_num_running_threads = 0;
|
m_num_running_threads = 0;
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ namespace ams::kern {
|
|||||||
RescheduleCurrentCore();
|
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) {
|
u64 KScheduler::UpdateHighestPriorityThread(KThread *highest_thread) {
|
||||||
if (KThread *prev_highest_thread = m_state.highest_priority_thread; AMS_LIKELY(prev_highest_thread != 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)) {
|
if (AMS_LIKELY(prev_highest_thread != nullptr)) {
|
||||||
@@ -254,9 +247,24 @@ namespace ams::kern {
|
|||||||
|
|
||||||
MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread);
|
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. */
|
/* Switch the current process, if we're switching processes. */
|
||||||
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
||||||
KProcess::Switch(cur_process, next_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. */
|
/* Set the new thread. */
|
||||||
|
|||||||
295
libraries/libmesosphere/source/kern_k_system_control_base.cpp
Normal file
295
libraries/libmesosphere/source/kern_k_system_control_base.cpp
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
#include <mesosphere/arch/arm64/kern_secure_monitor_base.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
/* Initialization. */
|
||||||
|
size_t KSystemControlBase::Init::GetRealMemorySize() {
|
||||||
|
return ams::kern::MainMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KSystemControlBase::Init::GetIntendedMemorySize() {
|
||||||
|
return ams::kern::MainMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPhysicalAddress KSystemControlBase::Init::GetKernelPhysicalBaseAddress(KPhysicalAddress base_address) {
|
||||||
|
const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
|
||||||
|
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||||
|
if (intended_dram_size * 2 < real_dram_size) {
|
||||||
|
return base_address;
|
||||||
|
} else {
|
||||||
|
return base_address + ((real_dram_size - intended_dram_size) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::Init::GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out) {
|
||||||
|
*out = {
|
||||||
|
.address = GetInteger(KSystemControl::Init::GetKernelPhysicalBaseAddress(ams::kern::MainMemoryAddress)) + KSystemControl::Init::GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax,
|
||||||
|
._08 = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool KSystemControlBase::Init::ShouldIncreaseThreadResourceLimit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t KSystemControlBase::Init::GetApplicationPoolSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KSystemControlBase::Init::GetAppletPoolSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t KSystemControlBase::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 KSystemControlBase::Init::GetDebugLogUartPort() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<0, false>(core_id, entrypoint, arg)) == 0);
|
||||||
|
#else
|
||||||
|
AMS_INFINITE_LOOP();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Randomness for Initialization. */
|
||||||
|
void KSystemControlBase::Init::GenerateRandom(u64 *dst, size_t count) {
|
||||||
|
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||||
|
const u64 seed = KHardwareTimer::GetTick();
|
||||||
|
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||||
|
s_initialized_random_generator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
dst[i] = s_random_generator.GenerateRandomU64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 KSystemControlBase::Init::GenerateRandomRange(u64 min, u64 max) {
|
||||||
|
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||||
|
const u64 seed = KHardwareTimer::GetTick();
|
||||||
|
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||||
|
s_initialized_random_generator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System Initialization. */
|
||||||
|
void KSystemControlBase::InitializePhase1(bool skip_target_system) {
|
||||||
|
/* Initialize the rng, if we somehow haven't already. */
|
||||||
|
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||||
|
const u64 seed = KHardwareTimer::GetTick();
|
||||||
|
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||||
|
s_initialized_random_generator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure KTargetSystem, if we haven't already by an implementation SystemControl. */
|
||||||
|
if (!skip_target_system) {
|
||||||
|
/* Set IsDebugMode. */
|
||||||
|
{
|
||||||
|
KTargetSystem::SetIsDebugMode(true);
|
||||||
|
|
||||||
|
/* If debug mode, we want to initialize uart logging. */
|
||||||
|
KTargetSystem::EnableDebugLogging(true);
|
||||||
|
KDebugLog::Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Kernel Configuration. */
|
||||||
|
{
|
||||||
|
KTargetSystem::EnableDebugMemoryFill(false);
|
||||||
|
KTargetSystem::EnableUserExceptionHandlers(true);
|
||||||
|
KTargetSystem::EnableDynamicResourceLimits(true);
|
||||||
|
KTargetSystem::EnableUserPmuAccess(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Kernel Debugging. */
|
||||||
|
{
|
||||||
|
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
|
||||||
|
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
|
||||||
|
KTargetSystem::EnableKernelDebugging(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System ResourceLimit initialization. */
|
||||||
|
{
|
||||||
|
/* Construct the resource limit object. */
|
||||||
|
KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit();
|
||||||
|
KAutoObject::Create<KResourceLimit>(std::addressof(sys_res_limit));
|
||||||
|
sys_res_limit.Initialize();
|
||||||
|
|
||||||
|
/* Set the initial limits. */
|
||||||
|
const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
|
||||||
|
const auto &slab_counts = init::GetSlabResourceCounts();
|
||||||
|
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
|
||||||
|
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
|
||||||
|
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent));
|
||||||
|
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory));
|
||||||
|
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession));
|
||||||
|
|
||||||
|
/* Reserve system memory. */
|
||||||
|
MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::InitializePhase2() {
|
||||||
|
/* Initialize KTrace. */
|
||||||
|
if constexpr (IsKTraceEnabled) {
|
||||||
|
const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion();
|
||||||
|
KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 KSystemControlBase::GetCreateProcessMemoryPool() {
|
||||||
|
return KMemoryManager::Pool_System;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Privileged Access. */
|
||||||
|
void KSystemControlBase::ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
|
||||||
|
/* TODO */
|
||||||
|
MESOSPHERE_UNUSED(out, address, mask, value);
|
||||||
|
MESOSPHERE_UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSystemControlBase::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
|
||||||
|
MESOSPHERE_UNUSED(out, address, mask, value);
|
||||||
|
return svc::ResultNotImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Randomness. */
|
||||||
|
void KSystemControlBase::GenerateRandom(u64 *dst, size_t count) {
|
||||||
|
KScopedInterruptDisable intr_disable;
|
||||||
|
KScopedSpinLock lk(s_random_lock);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
dst[i] = s_random_generator.GenerateRandomU64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 KSystemControlBase::GenerateRandomRange(u64 min, u64 max) {
|
||||||
|
KScopedInterruptDisable intr_disable;
|
||||||
|
KScopedSpinLock lk(s_random_lock);
|
||||||
|
|
||||||
|
return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 KSystemControlBase::GenerateRandomU64() {
|
||||||
|
KScopedInterruptDisable intr_disable;
|
||||||
|
KScopedSpinLock lk(s_random_lock);
|
||||||
|
|
||||||
|
return s_random_generator.GenerateRandomU64();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::SleepSystem() {
|
||||||
|
MESOSPHERE_LOG("SleepSystem() was called\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::StopSystem(void *) {
|
||||||
|
MESOSPHERE_LOG("KSystemControlBase::StopSystem\n");
|
||||||
|
AMS_INFINITE_LOOP();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||||
|
void KSystemControlBase::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
/* Get the function id for the current call. */
|
||||||
|
u64 function_id = args->r[0];
|
||||||
|
|
||||||
|
/* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */
|
||||||
|
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||||
|
auto *bim = page_table.GetBlockInfoManager();
|
||||||
|
|
||||||
|
constexpr size_t MaxMappedRegisters = 7;
|
||||||
|
std::array<KPageGroup, MaxMappedRegisters> page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), };
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
||||||
|
const size_t reg_id = i + 1;
|
||||||
|
if (function_id & (1ul << (8 + reg_id))) {
|
||||||
|
/* Create and open a new page group for the address. */
|
||||||
|
KVirtualAddress virt_addr = args->r[reg_id];
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) {
|
||||||
|
/* Translate the virtual address to a physical address. */
|
||||||
|
const auto it = page_groups[i].begin();
|
||||||
|
MESOSPHERE_ASSERT(it != page_groups[i].end());
|
||||||
|
MESOSPHERE_ASSERT(it->GetNumPages() == 1);
|
||||||
|
|
||||||
|
args->r[reg_id] = GetInteger(it->GetAddress()) | (GetInteger(virt_addr) & (PageSize - 1));
|
||||||
|
} else {
|
||||||
|
/* If we couldn't map, we should clear the address. */
|
||||||
|
args->r[reg_id] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invoke the secure monitor. */
|
||||||
|
KSystemControl::CallSecureMonitorFromUserImpl(args);
|
||||||
|
|
||||||
|
/* Make sure that we close any pages that we opened. */
|
||||||
|
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
||||||
|
page_groups[i].Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
/* By default, we don't actually support secure monitor, so just set args to a failure code. */
|
||||||
|
args->r[0] = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Secure Memory. */
|
||||||
|
size_t KSystemControlBase::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||||
|
MESOSPHERE_UNUSED(pool);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSystemControlBase::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
|
||||||
|
/* Ensure the size is aligned. */
|
||||||
|
constexpr size_t Alignment = PageSize;
|
||||||
|
R_UNLESS(util::IsAligned(size, Alignment), svc::ResultInvalidSize());
|
||||||
|
|
||||||
|
/* Allocate the memory. */
|
||||||
|
const size_t num_pages = size / PageSize;
|
||||||
|
const KPhysicalAddress paddr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(num_pages, Alignment / PageSize, KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool), KMemoryManager::Direction_FromFront));
|
||||||
|
R_UNLESS(paddr != Null<KPhysicalAddress>, svc::ResultOutOfMemory());
|
||||||
|
|
||||||
|
*out = KPageTable::GetHeapVirtualAddress(paddr);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControlBase::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) {
|
||||||
|
/* Ensure the size is aligned. */
|
||||||
|
constexpr size_t Alignment = PageSize;
|
||||||
|
MESOSPHERE_UNUSED(pool);
|
||||||
|
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), Alignment));
|
||||||
|
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, Alignment));
|
||||||
|
|
||||||
|
/* Close the secure region's pages. */
|
||||||
|
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1315,10 +1315,6 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KThreadContext *KThread::GetContextForSchedulerLoop() {
|
|
||||||
return std::addressof(this->GetContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
KThread *KThread::GetThreadFromId(u64 thread_id) {
|
KThread *KThread::GetThreadFromId(u64 thread_id) {
|
||||||
/* Lock the list. */
|
/* Lock the list. */
|
||||||
KThread::ListAccessor accessor;
|
KThread::ListAccessor accessor;
|
||||||
|
|||||||
@@ -139,14 +139,20 @@ namespace ams::kern {
|
|||||||
PrintMemoryRegion(" InitPageTable", KMemoryLayout::GetKernelInitPageTableRegionPhysicalExtents());
|
PrintMemoryRegion(" InitPageTable", KMemoryLayout::GetKernelInitPageTableRegionPhysicalExtents());
|
||||||
PrintMemoryRegion(" MemoryPoolRegion", KMemoryLayout::GetKernelPoolPartitionRegionPhysicalExtents());
|
PrintMemoryRegion(" MemoryPoolRegion", KMemoryLayout::GetKernelPoolPartitionRegionPhysicalExtents());
|
||||||
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||||
PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
|
|
||||||
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
|
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
|
||||||
PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents());
|
PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
|
||||||
PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents());
|
if (KMemoryLayout::HasKernelSystemNonSecurePoolRegion()) {
|
||||||
PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
|
PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents());
|
||||||
|
}
|
||||||
|
if (KMemoryLayout::HasKernelAppletPoolRegion()) {
|
||||||
|
PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents());
|
||||||
|
}
|
||||||
|
if (KMemoryLayout::HasKernelApplicationPoolRegion()) {
|
||||||
|
PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
|
|
||||||
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
|
PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents());
|
||||||
|
PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents());
|
||||||
PrintMemoryRegion(" Unsafe", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
|
PrintMemoryRegion(" Unsafe", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents());
|
||||||
}
|
}
|
||||||
if constexpr (IsKTraceEnabled) {
|
if constexpr (IsKTraceEnabled) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace ams::kern::svc {
|
|||||||
size_t remaining = size;
|
size_t remaining = size;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
/* Get a contiguous range to operate on. */
|
/* Get a contiguous range to operate on. */
|
||||||
KPageTableBase::MemoryRange contig_range = {};
|
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
||||||
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
||||||
|
|
||||||
/* Close the range when we're done operating on it. */
|
/* Close the range when we're done operating on it. */
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ $(OFILES) : $(GCH_FILES)
|
|||||||
$(OFILES_SRC) : $(HFILES_BIN)
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
ams_environment_weak.o: CXXFLAGS += -fno-lto
|
ams_environment_weak.o: CXXFLAGS += -fno-lto
|
||||||
|
hos_version_api_weak_for_unit_test.o: CXXFLAGS += -fno-lto
|
||||||
pm_info_api_weak.o: CXXFLAGS += -fno-lto
|
pm_info_api_weak.o: CXXFLAGS += -fno-lto
|
||||||
hos_stratosphere_api.o: CXXFLAGS += -fno-lto
|
hos_stratosphere_api.o: CXXFLAGS += -fno-lto
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
|
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Result SetHeapSize(uintptr_t *out_address, ::ams::svc::Size size) {
|
||||||
|
static_assert(sizeof(::ams::svc::Address) == sizeof(uintptr_t));
|
||||||
|
return ::svcSetHeapSize(reinterpret_cast<void **>(out_address), size);
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
|
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
|
||||||
return ::svcSetMemoryPermission(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(perm));
|
return ::svcSetMemoryPermission(reinterpret_cast<void *>(static_cast<uintptr_t>(address)), size, static_cast<u32>(perm));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,9 +89,8 @@ namespace ams::fs {
|
|||||||
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
|
||||||
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
|
||||||
|
|
||||||
/* TODO: Libnx binding for DeleteSaveDataFileSystemBySaveDataAttribute */
|
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
|
||||||
AMS_UNUSED(space_id, attribute);
|
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
|
||||||
AMS_ABORT();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace ams::hos {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUnitTestProgramForSetVersion();
|
||||||
void InitializeVersionInternal(bool allow_approximate);
|
void InitializeVersionInternal(bool allow_approximate);
|
||||||
|
|
||||||
void InitializeForStratosphere() {
|
void InitializeForStratosphere() {
|
||||||
@@ -58,7 +59,7 @@ namespace ams::hos {
|
|||||||
hos::InitializeVersionInternal(CanAllowTemporaryApproximateVersion());
|
hos::InitializeVersionInternal(CanAllowTemporaryApproximateVersion());
|
||||||
|
|
||||||
/* Check that we're running under mesosphere. */
|
/* Check that we're running under mesosphere. */
|
||||||
AMS_ABORT_UNLESS(svc::IsKernelMesosphere());
|
AMS_ABORT_UNLESS(IsUnitTestProgramForSetVersion() || svc::IsKernelMesosphere());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,51 +61,61 @@ namespace ams::hos {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUnitTestProgramForSetVersion();
|
||||||
|
|
||||||
void InitializeVersionInternal(bool allow_approximate) {
|
void InitializeVersionInternal(bool allow_approximate) {
|
||||||
/* Get the current (and previous approximation of) target firmware. */
|
hos::Version current = hos::Version_Current;
|
||||||
hos::Version prev, current;
|
|
||||||
bool has_prev = false;
|
|
||||||
{
|
|
||||||
/* Acquire exclusive access to set hos version. */
|
|
||||||
std::scoped_lock lk(g_hos_init_lock);
|
|
||||||
|
|
||||||
/* Save the previous value of g_hos_version. */
|
/* If we're unit testing, just set the version and move on. */
|
||||||
prev = g_hos_version;
|
if (IsUnitTestProgramForSetVersion()) {
|
||||||
has_prev = g_set_hos_version;
|
g_hos_version = hos::Version_Current;
|
||||||
|
|
||||||
/* Set hos version = exosphere api version target firmware. */
|
|
||||||
g_hos_version = static_cast<hos::Version>(GetExosphereApiInfo(allow_approximate).GetTargetFirmware());
|
|
||||||
|
|
||||||
/* Save the current value of g_hos_version. */
|
|
||||||
current = g_hos_version;
|
|
||||||
|
|
||||||
/* Note that we've set a previous hos version. */
|
|
||||||
g_set_hos_version = true;
|
g_set_hos_version = true;
|
||||||
}
|
} else {
|
||||||
|
/* Get the current (and previous approximation of) target firmware. */
|
||||||
|
hos::Version prev;
|
||||||
|
bool has_prev = false;
|
||||||
|
{
|
||||||
|
/* Acquire exclusive access to set hos version. */
|
||||||
|
std::scoped_lock lk(g_hos_init_lock);
|
||||||
|
|
||||||
/* Ensure that this is a hos version we can sanely *try* to run. */
|
/* Save the previous value of g_hos_version. */
|
||||||
/* To be friendly, we will only require that we recognize the major and minor versions. */
|
prev = g_hos_version;
|
||||||
/* We can consider only recognizing major in the future, but micro seems safe to ignore as */
|
has_prev = g_set_hos_version;
|
||||||
/* there are no breaking IPC changes in minor updates. */
|
|
||||||
{
|
|
||||||
constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF;
|
|
||||||
constexpr u32 MaxMinor = (static_cast<u32>(hos::Version_Max) >> 16) & 0xFF;
|
|
||||||
|
|
||||||
const u32 major = (static_cast<u32>(current) >> 24) & 0xFF;
|
/* Set hos version = exosphere api version target firmware. */
|
||||||
const u32 minor = (static_cast<u32>(current) >> 16) & 0xFF;
|
g_hos_version = static_cast<hos::Version>(GetExosphereApiInfo(allow_approximate).GetTargetFirmware());
|
||||||
|
|
||||||
const bool is_safely_tryable_version = (current <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor);
|
/* Save the current value of g_hos_version. */
|
||||||
AMS_ABORT_UNLESS(is_safely_tryable_version);
|
current = g_hos_version;
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure that this is a hos version compatible with previous approximations. */
|
/* Note that we've set a previous hos version. */
|
||||||
if (has_prev) {
|
g_set_hos_version = true;
|
||||||
AMS_ABORT_UNLESS(current >= prev);
|
}
|
||||||
|
|
||||||
const u32 current_major = (static_cast<u32>(current) >> 24) & 0xFF;
|
/* Ensure that this is a hos version we can sanely *try* to run. */
|
||||||
const u32 prev_major = (static_cast<u32>(prev) >> 24) & 0xFF;
|
/* To be friendly, we will only require that we recognize the major and minor versions. */
|
||||||
|
/* We can consider only recognizing major in the future, but micro seems safe to ignore as */
|
||||||
|
/* there are no breaking IPC changes in minor updates. */
|
||||||
|
{
|
||||||
|
constexpr u32 MaxMajor = (static_cast<u32>(hos::Version_Max) >> 24) & 0xFF;
|
||||||
|
constexpr u32 MaxMinor = (static_cast<u32>(hos::Version_Max) >> 16) & 0xFF;
|
||||||
|
|
||||||
AMS_ABORT_UNLESS(current_major == prev_major);
|
const u32 major = (static_cast<u32>(current) >> 24) & 0xFF;
|
||||||
|
const u32 minor = (static_cast<u32>(current) >> 16) & 0xFF;
|
||||||
|
|
||||||
|
const bool is_safely_tryable_version = (current <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor);
|
||||||
|
AMS_ABORT_UNLESS(is_safely_tryable_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that this is a hos version compatible with previous approximations. */
|
||||||
|
if (has_prev) {
|
||||||
|
AMS_ABORT_UNLESS(current >= prev);
|
||||||
|
|
||||||
|
const u32 current_major = (static_cast<u32>(current) >> 24) & 0xFF;
|
||||||
|
const u32 prev_major = (static_cast<u32>(prev) >> 24) & 0xFF;
|
||||||
|
|
||||||
|
AMS_ABORT_UNLESS(current_major == prev_major);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the version for libnx. */
|
/* Set the version for libnx. */
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::hos {
|
||||||
|
|
||||||
|
WEAK_SYMBOL bool IsUnitTestProgramForSetVersion() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ extern "C" {
|
|||||||
constinit u32 __nx_fs_num_sessions = 1;
|
constinit u32 __nx_fs_num_sessions = 1;
|
||||||
constinit u32 __nx_applet_type = AppletType_None;
|
constinit u32 __nx_applet_type = AppletType_None;
|
||||||
|
|
||||||
|
constinit bool __nx_fsdev_support_cwd = false;
|
||||||
|
|
||||||
extern int __system_argc;
|
extern int __system_argc;
|
||||||
extern char** __system_argv;
|
extern char** __system_argv;
|
||||||
|
|
||||||
@@ -38,6 +40,8 @@ namespace ams {
|
|||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
|
void InitializeSystemModuleBeforeConstructors();
|
||||||
|
|
||||||
void InitializeSystemModule();
|
void InitializeSystemModule();
|
||||||
void FinalizeSystemModule();
|
void FinalizeSystemModule();
|
||||||
|
|
||||||
@@ -66,6 +70,9 @@ extern "C" void __libnx_initheap(void) {
|
|||||||
extern "C" void __appInit(void) {
|
extern "C" void __appInit(void) {
|
||||||
/* The very first thing all stratosphere code must do is initialize the os library. */
|
/* The very first thing all stratosphere code must do is initialize the os library. */
|
||||||
::ams::hos::InitializeForStratosphere();
|
::ams::hos::InitializeForStratosphere();
|
||||||
|
|
||||||
|
/* Perform pre-C++ constructor init. */
|
||||||
|
::ams::init::InitializeSystemModuleBeforeConstructors();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void __appExit(void) {
|
extern "C" void __appExit(void) {
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
namespace ams::init {
|
namespace ams::init {
|
||||||
|
|
||||||
|
WEAK_SYMBOL void InitializeSystemModuleBeforeConstructors() {
|
||||||
|
/* This should only be used in exceptional circumstances. */
|
||||||
|
}
|
||||||
|
|
||||||
WEAK_SYMBOL void InitializeSystemModule() {
|
WEAK_SYMBOL void InitializeSystemModule() {
|
||||||
/* TODO: What should we do here, if anything? */
|
/* TODO: What should we do here, if anything? */
|
||||||
/* Nintendo does nndiagStartup(); nn::diag::InitializeSystemProcessAbortObserver(); */
|
/* Nintendo does nndiagStartup(); nn::diag::InitializeSystemProcessAbortObserver(); */
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
|
|||||||
constexpr const sm::ServiceName ServiceNameForSystemProcess = sm::ServiceName::Encode("sprof:sp");
|
constexpr const sm::ServiceName ServiceNameForSystemProcess = sm::ServiceName::Encode("sprof:sp");
|
||||||
|
|
||||||
constexpr inline size_t BgAgentSessionCountMax = 2;
|
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;
|
constexpr inline size_t SessionCountMax = BgAgentSessionCountMax + SystemProcessSessionCountMax;
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ namespace ams::sprofile::srv {
|
|||||||
|
|
||||||
struct ServerManagerOptions {
|
struct ServerManagerOptions {
|
||||||
static constexpr size_t PointerBufferSize = 0x0;
|
static constexpr size_t PointerBufferSize = 0x0;
|
||||||
static constexpr size_t MaxDomains = SessionCountMax; /* NOTE: Official is 3 */
|
static constexpr size_t MaxDomains = SessionCountMax; /* NOTE: Official is 9 */
|
||||||
static constexpr size_t MaxDomainObjects = 16; /* NOTE: Official is 8 */
|
static constexpr size_t MaxDomainObjects = 16; /* NOTE: Official is 14 */
|
||||||
static constexpr bool CanDeferInvokeRequest = false;
|
static constexpr bool CanDeferInvokeRequest = false;
|
||||||
static constexpr bool CanManageMitmServers = false;
|
static constexpr bool CanManageMitmServers = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
#include "sprofile_srv_types.hpp"
|
#include "sprofile_srv_types.hpp"
|
||||||
|
|
||||||
#define AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO(C, H) \
|
#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, 0, Result, ImportProfile, (const sprofile::srv::ProfileDataForImportData &import), (import)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, Commit, (), ()) \
|
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, 2, Result, ImportMetadata, (const sprofile::srv::ProfileMetadataForImportMetadata &import), (import)) \
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, IProfileImporter, AMS_SPROFILE_I_PROFILE_IMPORTER_INTERFACE_INFO)
|
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, 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, 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, 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)
|
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"
|
#include "sprofile_srv_i_profile_importer.hpp"
|
||||||
|
|
||||||
#define AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO(C, H) \
|
#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, 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, 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, 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, 2000, Result, Reset, (), ())
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sprofile, ISprofileServiceForBgAgent, AMS_SPROFILE_I_SPROFILE_SERVICE_FOR_BG_AGENT_INTERFACE_INFO)
|
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) {
|
bool HasProfile(Identifier id0, Identifier id1) {
|
||||||
/* Require that we have metadata. */
|
/* Require that we have metadata. */
|
||||||
if (m_metadata.has_value()) {
|
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];
|
const auto &entry = m_metadata->entries[i];
|
||||||
if (entry.identifier_0 == id0 && entry.identifier_1 == id1) {
|
if (entry.identifier_0 == id0 && entry.identifier_1 == id1) {
|
||||||
return true;
|
return true;
|
||||||
@@ -63,7 +63,7 @@ namespace ams::sprofile::srv {
|
|||||||
m_revision_key = meta.revision_key;
|
m_revision_key = meta.revision_key;
|
||||||
|
|
||||||
/* Import all profiles. */
|
/* 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];
|
const auto &import_entry = meta.entries[i];
|
||||||
if (!this->HasProfile(import_entry.identifier_0, import_entry.identifier_1)) {
|
if (!this->HasProfile(import_entry.identifier_0, import_entry.identifier_1)) {
|
||||||
m_importing_profiles[m_importing_count++] = import_entry.identifier_0;
|
m_importing_profiles[m_importing_count++] = import_entry.identifier_0;
|
||||||
|
|||||||
@@ -19,16 +19,16 @@
|
|||||||
|
|
||||||
namespace ams::sprofile::srv {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &data) {
|
Result ProfileImporterImpl::ImportProfile(const sprofile::srv::ProfileDataForImportData &import) {
|
||||||
return m_manager->ImportProfile(data);
|
return m_manager->ImportProfile(import);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ProfileImporterImpl::Commit() {
|
Result ProfileImporterImpl::Commit() {
|
||||||
return m_manager->Commit();
|
return m_manager->Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
|
Result ProfileImporterImpl::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
|
||||||
return m_manager->ImportMetadata(data);
|
return m_manager->ImportMetadata(import);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,14 @@ namespace ams::sprofile::srv {
|
|||||||
ProfileManager *m_manager;
|
ProfileManager *m_manager;
|
||||||
public:
|
public:
|
||||||
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
|
ProfileImporterImpl(ProfileManager *manager) : m_manager(manager) { /* ... */ }
|
||||||
|
|
||||||
|
~ProfileImporterImpl() {
|
||||||
|
m_manager->CloseProfileImporter();
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &import);
|
||||||
Result Commit();
|
Result Commit();
|
||||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import);
|
||||||
};
|
};
|
||||||
static_assert(IsIProfileImporter<ProfileImporterImpl>);
|
static_assert(IsIProfileImporter<ProfileImporterImpl>);
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,17 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
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. */
|
/* Acquire locks. */
|
||||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||||
std::scoped_lock lk2(m_fs_mutex);
|
std::scoped_lock lk2(m_fs_mutex);
|
||||||
@@ -159,25 +169,39 @@ namespace ams::sprofile::srv {
|
|||||||
/* Check that we have an importer. */
|
/* Check that we have an importer. */
|
||||||
R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
|
R_UNLESS(m_profile_importer.has_value(), sprofile::ResultInvalidState());
|
||||||
|
|
||||||
/* Check that the metadata we're importing is valid. */
|
/* Check that the metadata we're importing is a valid version. */
|
||||||
R_UNLESS(data.data.version == ProfileDataVersion, sprofile::ResultInvalidDataVersion());
|
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. */
|
/* 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. */
|
/* 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. */
|
/* Create temporary directories. */
|
||||||
R_TRY(this->EnsureTemporaryDirectories());
|
R_TRY(this->EnsureTemporaryDirectories());
|
||||||
|
|
||||||
/* Create profile. */
|
/* Create profile. */
|
||||||
char path[0x30];
|
char path[0x30];
|
||||||
CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, data.identifier_0);
|
CreateTemporaryProfilePath(path, sizeof(path), m_save_data_info.mount_name, import.header.identifier_0);
|
||||||
R_TRY(WriteFile(path, std::addressof(data.data), sizeof(data.data)));
|
R_TRY(WriteFile(path, std::addressof(import.data), sizeof(import.data)));
|
||||||
|
|
||||||
/* Set profile imported. */
|
/* Set profile imported. */
|
||||||
m_profile_importer->OnImportProfile(data.identifier_0);
|
m_profile_importer->OnImportProfile(import.header.identifier_0);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +255,7 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data) {
|
Result ProfileManager::ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &import) {
|
||||||
/* Acquire locks. */
|
/* Acquire locks. */
|
||||||
std::scoped_lock lk1(m_profile_importer_mutex);
|
std::scoped_lock lk1(m_profile_importer_mutex);
|
||||||
std::scoped_lock lk2(m_fs_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.has_value(), sprofile::ResultInvalidState());
|
||||||
R_UNLESS(m_profile_importer->CanImportMetadata(), sprofile::ResultInvalidState());
|
R_UNLESS(m_profile_importer->CanImportMetadata(), sprofile::ResultInvalidState());
|
||||||
|
|
||||||
/* Check that the metadata we're importing is valid. */
|
/* Check that the metadata we're importing is a valid version. */
|
||||||
R_UNLESS(data.metadata.version == ProfileMetadataVersion, sprofile::ResultInvalidMetadataVersion());
|
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. */
|
/* Create temporary directories. */
|
||||||
R_TRY(this->EnsureTemporaryDirectories());
|
R_TRY(this->EnsureTemporaryDirectories());
|
||||||
@@ -249,10 +288,10 @@ namespace ams::sprofile::srv {
|
|||||||
/* Create metadata. */
|
/* Create metadata. */
|
||||||
char path[0x30];
|
char path[0x30];
|
||||||
CreateTemporaryMetadataPath(path, sizeof(path), m_save_data_info.mount_name);
|
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. */
|
/* Import the metadata. */
|
||||||
m_profile_importer->ImportMetadata(data.metadata);
|
m_profile_importer->ImportMetadata(import.metadata);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,13 +348,13 @@ namespace ams::sprofile::srv {
|
|||||||
std::scoped_lock lk2(m_general_mutex);
|
std::scoped_lock lk2(m_general_mutex);
|
||||||
|
|
||||||
/* Load the desired profile. */
|
/* Load the desired profile. */
|
||||||
R_TRY(this->LoadProfile(profile));
|
if (R_SUCCEEDED(this->LoadProfile(profile))) {
|
||||||
|
/* Find the specified key. */
|
||||||
/* 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) {
|
||||||
for (auto i = 0u; i < m_service_profile->data.num_entries; ++i) {
|
if (m_service_profile->data.entries[i].key == key) {
|
||||||
if (m_service_profile->data.entries[i].key == key) {
|
*out = m_service_profile->data.entries[i];
|
||||||
*out = m_service_profile->data.entries[i];
|
return ResultSuccess();
|
||||||
return ResultSuccess();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,6 +465,8 @@ namespace ams::sprofile::srv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ProfileManager::OnCommitted() {
|
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 we need to, invalidate the loaded service profile. */
|
||||||
if (m_service_profile.has_value()) {
|
if (m_service_profile.has_value()) {
|
||||||
for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
|
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. */
|
/* Reset profile metadata. */
|
||||||
m_profile_metadata = util::nullopt;
|
m_profile_metadata = util::nullopt;
|
||||||
|
|
||||||
@@ -445,6 +484,9 @@ namespace ams::sprofile::srv {
|
|||||||
for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
|
for (auto i = 0; i < m_profile_importer->GetImportingCount(); ++i) {
|
||||||
m_update_observer_manager.OnUpdate(m_profile_importer->GetImportingProfile(i));
|
m_update_observer_manager.OnUpdate(m_profile_importer->GetImportingProfile(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset profile importer. */
|
||||||
|
m_profile_importer = util::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ProfileManager::EnsurePrimaryDirectories() {
|
Result ProfileManager::EnsurePrimaryDirectories() {
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ namespace ams::sprofile::srv {
|
|||||||
Result ResetSaveData();
|
Result ResetSaveData();
|
||||||
|
|
||||||
Result OpenProfileImporter();
|
Result OpenProfileImporter();
|
||||||
|
void CloseProfileImporter();
|
||||||
|
|
||||||
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
Result ImportProfile(const sprofile::srv::ProfileDataForImportData &data);
|
||||||
Result Commit();
|
Result Commit();
|
||||||
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
Result ImportMetadata(const sprofile::srv::ProfileMetadataForImportMetadata &data);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
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. */
|
/* Check size. */
|
||||||
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
|
R_UNLESS(out.GetSize() >= arg.metadata.num_entries, sprofile::ResultInvalidArgument());
|
||||||
|
|
||||||
@@ -42,25 +42,31 @@ namespace ams::sprofile::srv {
|
|||||||
sprofile::srv::ProfileMetadata primary_metadata;
|
sprofile::srv::ProfileMetadata primary_metadata;
|
||||||
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
|
R_TRY_CATCH(m_profile_manager->LoadPrimaryMetadata(std::addressof(primary_metadata))) {
|
||||||
R_CATCH(fs::ResultPathNotFound) {
|
R_CATCH(fs::ResultPathNotFound) {
|
||||||
/* If we have no metadata, we can't get any entries. */
|
/* It's okay if we have no primary metadata -- this means that all profiles are importable. */
|
||||||
*out_count = 0;
|
primary_metadata.num_entries = 0;
|
||||||
return ResultSuccess();
|
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} 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;
|
u32 count = 0;
|
||||||
for (u32 i = 0; i < arg.metadata.num_entries; ++i) {
|
for (u32 i = 0; i < arg.metadata.num_entries; ++i) {
|
||||||
const auto &arg_entry = arg.metadata.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) {
|
for (u32 j = 0; j < primary_metadata.num_entries; ++j) {
|
||||||
const auto &pri_entry = primary_metadata.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) {
|
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;
|
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. */
|
/* Set output count. */
|
||||||
@@ -68,6 +74,7 @@ namespace ams::sprofile::srv {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
|
Result ServiceForBgAgent::IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key) {
|
||||||
/* Load primary metadata. */
|
/* Load primary metadata. */
|
||||||
bool loaded_metadata = true;
|
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) { /* ... */ }
|
constexpr ServiceForBgAgent(MemoryResource *mr, ProfileManager *pm) : m_memory_resource(mr), m_profile_manager(pm) { /* ... */ }
|
||||||
public:
|
public:
|
||||||
Result OpenProfileImporter(sf::Out<sf::SharedPointer<::ams::sprofile::IProfileImporter>> out);
|
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 IsUpdateNeeded(sf::Out<bool> out, Identifier revision_key);
|
||||||
Result Reset();
|
Result Reset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,34 +18,11 @@
|
|||||||
|
|
||||||
namespace ams::sprofile::srv {
|
namespace ams::sprofile::srv {
|
||||||
|
|
||||||
struct ProfileMetadataEntry {
|
constexpr inline const u32 ProfileFormatVersion = 1;
|
||||||
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 ProfileMetadataVersion = 0;
|
constexpr inline bool IsValidProfileFormatVersion(u32 version) {
|
||||||
|
return version == ProfileFormatVersion;
|
||||||
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;
|
|
||||||
|
|
||||||
enum ValueType : u8 {
|
enum ValueType : u8 {
|
||||||
ValueType_Byte = 0,
|
ValueType_Byte = 0,
|
||||||
@@ -69,15 +46,24 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ProfileDataEntry>::value);
|
static_assert(util::is_pod<ProfileDataEntry>::value);
|
||||||
static_assert(sizeof(ProfileDataEntry) == 0x10);
|
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 {
|
struct ProfileData {
|
||||||
u32 version;
|
|
||||||
u32 num_entries;
|
u32 num_entries;
|
||||||
u8 unk_08[0x28];
|
u8 unk_04[0x0C];
|
||||||
|
u8 unk_10[0x20];
|
||||||
ProfileDataEntry entries[(0x4000 - 0x30) / sizeof(ProfileDataEntry)];
|
ProfileDataEntry entries[(0x4000 - 0x30) / sizeof(ProfileDataEntry)];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ProfileData>::value);
|
static_assert(util::is_pod<ProfileData>::value);
|
||||||
static_assert(sizeof(ProfileData) == 0x4000);
|
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 {
|
struct ServiceProfile {
|
||||||
Identifier name;
|
Identifier name;
|
||||||
ProfileData data;
|
ProfileData data;
|
||||||
@@ -85,29 +71,82 @@ namespace ams::sprofile::srv {
|
|||||||
static_assert(util::is_pod<ServiceProfile>::value);
|
static_assert(util::is_pod<ServiceProfile>::value);
|
||||||
static_assert(sizeof(ServiceProfile) == 0x4008);
|
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 {
|
struct ProfileDataForImportData : public sf::LargeData, public sf::PrefersMapAliasTransferMode {
|
||||||
Identifier identifier_0;
|
struct {
|
||||||
Identifier identifier_1;
|
Identifier identifier_0;
|
||||||
u8 unk_0E[2];
|
Identifier identifier_1;
|
||||||
|
u8 unk_0E[2];
|
||||||
|
u32 version;
|
||||||
|
u8 unk_14[0x1C];
|
||||||
|
} header;
|
||||||
|
u8 hash[crypto::Md5Generator::HashSize];
|
||||||
ProfileData data;
|
ProfileData data;
|
||||||
u8 unk_4010[0x4400 - 0x4010];
|
u8 unk_4040[0x4400 - 0x4040];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ProfileDataForImportData>::value);
|
static_assert(util::is_pod<ProfileDataForImportData>::value);
|
||||||
static_assert(sizeof(ProfileDataForImportData) == 0x4400);
|
static_assert(sizeof(ProfileDataForImportData) == 0x4400);
|
||||||
|
|
||||||
struct ReadMetadataEntry {
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, header) == 0x00);
|
||||||
u8 unk[0x100];
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, hash) == 0x30);
|
||||||
};
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, data) == 0x40);
|
||||||
static_assert(util::is_pod<ReadMetadataEntry>::value);
|
static_assert(AMS_OFFSETOF(ProfileDataForImportData, unk_4040) == 0x4040);
|
||||||
static_assert(sizeof(ReadMetadataEntry) == 0x100);
|
|
||||||
|
|
||||||
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;
|
ProfileMetadata metadata;
|
||||||
ReadMetadataEntry entries[(0x8000 - sizeof(metadata)) / sizeof(ReadMetadataEntry)];
|
ProfileUrl profile_urls[50];
|
||||||
u8 unk_7FC0[0x40];
|
u8 unk_3EF0[0x8000 - 0x3EF0];
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<ReadMetadataArgument>::value);
|
static_assert(util::is_pod<ProfileMetadataForImportMetadata>::value);
|
||||||
static_assert(sizeof(ReadMetadataArgument) == 0x8000);
|
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_MAJOR 1
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 2
|
#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_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_MAJOR 13
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
#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_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_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_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_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
#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_0_3 = ATMOSPHERE_TARGET_FIRMWARE_12_0_3,
|
||||||
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
TargetFirmware_12_1_0 = ATMOSPHERE_TARGET_FIRMWARE_12_1_0,
|
||||||
TargetFirmware_13_0_0 = ATMOSPHERE_TARGET_FIRMWARE_13_0_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,
|
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <vapours/crypto/crypto_memory_compare.hpp>
|
#include <vapours/crypto/crypto_memory_compare.hpp>
|
||||||
#include <vapours/crypto/crypto_memory_clear.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_sha1_generator.hpp>
|
||||||
#include <vapours/crypto/crypto_sha256_generator.hpp>
|
#include <vapours/crypto/crypto_sha256_generator.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_encryptor.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>); */
|
||||||
|
|
||||||
|
}
|
||||||
@@ -80,3 +80,26 @@ namespace ams::impl {
|
|||||||
#define AMS_UNUSED(...) ::ams::impl::UnusedImpl(__VA_ARGS__)
|
#define AMS_UNUSED(...) ::ams::impl::UnusedImpl(__VA_ARGS__)
|
||||||
|
|
||||||
#define AMS_INFINITE_LOOP() do { __asm__ __volatile__("" ::: "memory"); } while (1)
|
#define AMS_INFINITE_LOOP() do { __asm__ __volatile__("" ::: "memory"); } while (1)
|
||||||
|
|
||||||
|
#define AMS__NARG__(...) AMS__NARG_I_(__VA_ARGS__,AMS__RSEQ_N())
|
||||||
|
#define AMS__NARG_I_(...) AMS__ARG_N(__VA_ARGS__)
|
||||||
|
#define AMS__ARG_N( \
|
||||||
|
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
|
||||||
|
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
|
||||||
|
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
|
||||||
|
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
|
||||||
|
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
|
||||||
|
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
|
||||||
|
_61,_62,_63,N,...) N
|
||||||
|
#define AMS__RSEQ_N() \
|
||||||
|
63,62,61,60, \
|
||||||
|
59,58,57,56,55,54,53,52,51,50, \
|
||||||
|
49,48,47,46,45,44,43,42,41,40, \
|
||||||
|
39,38,37,36,35,34,33,32,31,30, \
|
||||||
|
29,28,27,26,25,24,23,22,21,20, \
|
||||||
|
19,18,17,16,15,14,13,12,11,10, \
|
||||||
|
9,8,7,6,5,4,3,2,1,0
|
||||||
|
|
||||||
|
#define AMS__VMACRO_(name, n) name##_##n
|
||||||
|
#define AMS__VMACRO(name, n) AMS__VMACRO_(name, n)
|
||||||
|
#define AMS_VMACRO(func, ...) AMS__VMACRO(func, AMS__NARG__(__VA_ARGS__)) (__VA_ARGS__)
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ namespace ams::sprofile {
|
|||||||
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
|
R_DEFINE_ERROR_RESULT(MaxObservers, 623);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
|
R_DEFINE_ERROR_RESULT(InvalidMetadataVersion, 3210);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidMetadataHash, 3211);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidDataVersion, 3230);
|
R_DEFINE_ERROR_RESULT(InvalidDataVersion, 3230);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidDataHash, 3231);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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/svc/svc_types_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::svc::board::qemu::virt {
|
||||||
|
|
||||||
|
constexpr inline const s64 TicksPerSecond = 19'200'000;
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user