Compare commits

..

30 Commits

Author SHA1 Message Date
Michael Scire
b9c90b9234 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "4d0f1b792"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "4d0f1b792"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-29 17:47:35 -07:00
Michael Scire
94e18b8c93 ams: bump version to 1.2.2 2021-10-29 17:47:01 -07:00
Michael Scire
4e92687cab sprofile: various correctness fixes. 2021-10-29 17:38:44 -07:00
Michael Scire
2a0b99d9f9 sprofile: fix off-by-one in struct definition, fix GetImportableProfileUrls 2021-10-29 15:41:25 -07:00
Michael Scire
d1f3c4904b kern: fix minor assembly bugs, avoid unnecessary function call in KScheduler hotloop 2021-10-28 19:16:23 -07:00
Michael Scire
92321ccbc8 kern: fix 32-bit light ipc svc handler asm
Nintendo used to do what we were doing because the function wasn't directly in the handler table,
but we've always been directly in the handler table, so we were trashing the last four arguments to light ipc
when called from aarch32. Nothing uses this, but needed to be fixed.
2021-10-28 15:42:52 -07:00
Michael Scire
db3004e844 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "0a0bd74ca"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "0a0bd74ca"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-27 22:57:39 -07:00
Michael Scire
3e97e4addf init: disable fsdev cwd to prevent abort on fsdev usage (fsdev should not be used) 2021-10-27 22:57:09 -07:00
Michael Scire
4b7b33809f kern: optimize and bring into line with N our pstate.i management 2021-10-27 15:00:07 -07:00
Michael Scire
e81a1ce5a8 kern: audit (and fix) our hardware maintenance instructions to match official kernel 2021-10-27 12:31:53 -07:00
Michael Scire
fb59d0ad43 Unfuck emummc to not corrupt remote on subrepo push.
subrepo:
  subdir:   "emummc"
  merged:   "a9d569594"
upstream:
  origin:   "https://github.com/m4xw/emuMMC"
  branch:   "develop"
  commit:   "a9d569594"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-26 01:52:41 -07:00
Michael Scire
73b74b904f docs: add changelog for 1.2.1 2021-10-26 01:46:10 -07:00
Michael Scire
ed41b01b69 git subrepo push emummc
subrepo:
  subdir:   "emummc"
  merged:   "f11d22d74"
upstream:
  origin:   "https://github.com/m4xw/emuMMC"
  branch:   "develop"
  commit:   "f11d22d74"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-26 01:13:50 -07:00
Michael Scire
24143d8813 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "13c6987cc"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "13c6987cc"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-10-26 01:12:06 -07:00
Michael Scire
cb38b0b929 ams: bump version to 1.2.1 2021-10-26 01:09:16 -07:00
Michael Scire
dd04113f5d emummc/fusee: support 13.1.0 FS 2021-10-26 01:08:37 -07:00
Michael Scire
0e81eac9d1 sprof: update for 13.1.0 (format version 1) 2021-10-26 00:51:44 -07:00
Michael Scire
a14dc6ed89 crypto: implement md5, which now used by sprof 2021-10-25 23:15:50 -07:00
Michael Scire
9cc6be4d57 kern: other dmbs in kernel were already dmb ish 2021-10-25 17:38:50 -07:00
Michael Scire
1d5f66be56 kern: implement 13.1.0 kernel changes 2021-10-25 17:34:47 -07:00
Michael Scire
ebca23305e kern: simplify random bitmap selection to match latest Nintendo logic 2021-10-25 17:31:14 -07:00
Michael Scire
273f4a87ae kern: add (and use) generic KSystemControlBase 2021-10-25 17:31:14 -07:00
Michael Scire
1f8bf41f0b kern/test: add some scheduler tests (yields work correctly, all non-special priorities are cooperative/not pre-emptive 2021-10-25 17:31:14 -07:00
Michael Scire
ad03be9a38 hos: whoops 2021-10-25 17:31:14 -07:00
Michael Scire
d63be0737b hos: better safe than sorry 2021-10-25 17:31:14 -07:00
Michael Scire
018ae08409 hos: allow turning off ams extension hard-reqs for unit testing 2021-10-25 17:31:14 -07:00
Michael Scire
2a842791eb kern: add toggleable support for 40-bit physaddr caps 2021-10-25 17:31:14 -07:00
Michael Scire
d8a2b47b0a util: add trait/macro for is_constexpr_constructible 2021-10-25 17:31:14 -07:00
Michael Scire
7c1347e692 test: add tests for SetMemoryPermission 2021-10-25 17:31:14 -07:00
Michael Scire
0a58e803be kern/test: add wip qemu-virt board support to mesosphere 2021-10-25 17:31:14 -07:00
63 changed files with 1010 additions and 377 deletions

View File

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

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

2
emummc/README.md vendored
View File

@@ -2,7 +2,7 @@
*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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -52,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();
@@ -173,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();
@@ -186,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) {
@@ -209,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();

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) { /* ... */ }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -70,7 +70,7 @@ namespace ams::svc {
} }
} }
return util::ScaleByConstantFactor<s64, TicksPerSecond, NanoSecondsPerSecond>(ns); return util::ScaleByConstantFactorUp<s64, TicksPerSecond, NanoSecondsPerSecond>(ns);
} }
public: public:
constexpr ALWAYS_INLINE explicit Tick(s64 t = 0) : m_tick(t) { /* ... */ } constexpr ALWAYS_INLINE explicit Tick(s64 t = 0) : m_tick(t) { /* ... */ }

View File

@@ -58,7 +58,7 @@ namespace ams::svc {
/* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */ /* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */
/* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */ /* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(13); constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(13);
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 3); constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 4);
constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);

View File

@@ -256,7 +256,7 @@ namespace ams::util {
} }
template<typename T, T N, T D> template<typename T, T N, T D>
constexpr ALWAYS_INLINE T ScaleByConstantFactor(const T V) { constexpr ALWAYS_INLINE T ScaleByConstantFactorUp(const T V) {
/* Multiplying and dividing by large numerator/denominator can cause error to be introduced. */ /* Multiplying and dividing by large numerator/denominator can cause error to be introduced. */
/* This algorithm multiples/divides in stages, so as to mitigate this (particularly with large denominator). */ /* This algorithm multiples/divides in stages, so as to mitigate this (particularly with large denominator). */
@@ -279,4 +279,20 @@ namespace ams::util {
return (D * Quot_N * Quot_V) + (Quot_V * Rem_N) + (Rem_V * Quot_N) + rem_mult; return (D * Quot_N * Quot_V) + (Quot_V * Rem_N) + (Rem_V * Quot_N) + rem_mult;
} }
template<std::integral T>
constexpr ALWAYS_INLINE T RotateLeft(T v, int n) {
using Unsigned = typename std::make_unsigned<T>::type;
static_assert(sizeof(Unsigned) == sizeof(T));
return static_cast<T>(std::rotl<Unsigned>(static_cast<Unsigned>(v), n));
}
template<std::integral T>
constexpr ALWAYS_INLINE T RotateRight(T v, int n) {
using Unsigned = typename std::make_unsigned<T>::type;
static_assert(sizeof(Unsigned) == sizeof(T));
return static_cast<T>(std::rotr<Unsigned>(static_cast<Unsigned>(v), n));
}
} }

View File

@@ -20,11 +20,11 @@
namespace ams::util { namespace ams::util {
constexpr bool IsLittleEndian() { consteval bool IsLittleEndian() {
return std::endian::native == std::endian::little; return std::endian::native == std::endian::little;
} }
constexpr bool IsBigEndian() { consteval bool IsBigEndian() {
return std::endian::native == std::endian::big; return std::endian::native == std::endian::big;
} }

View File

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

View File

@@ -0,0 +1,255 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <vapours.hpp>
namespace ams::crypto::impl {
namespace {
struct Md5Constants {
static constexpr const u32 A = 0x67452301;
static constexpr const u32 B = 0xEFCDAB89;
static constexpr const u32 C = 0x98BADCFE;
static constexpr const u32 D = 0x10325476;
static constexpr const u32 T[] = {
0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391,
};
static constexpr u32 K[] = {
0x1, 0x6, 0xB, 0x0,
0x5, 0xA, 0xF, 0x4,
0x9, 0xE, 0x3, 0x8,
0xD, 0x2, 0x7, 0xC,
0x5, 0x8, 0xB, 0xE,
0x1, 0x4, 0x7, 0xA,
0xD, 0x0, 0x3, 0x6,
0x9, 0xC, 0xF, 0x2,
0x0, 0x7, 0xE, 0x5,
0xC, 0x3, 0xA, 0x1,
0x8, 0xF, 0x6, 0xD,
0x4, 0xB, 0x2, 0x9,
};
static constexpr u8 Padding[] = {
0x80
};
};
constexpr ALWAYS_INLINE u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); }
constexpr ALWAYS_INLINE u32 G(u32 x, u32 y, u32 z) { return (x & z) | (y & (~z)); }
constexpr ALWAYS_INLINE u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; }
constexpr ALWAYS_INLINE u32 I(u32 x, u32 y, u32 z) { return y ^ (x | (~z)); }
constexpr ALWAYS_INLINE u32 CalculateRound1(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + F(b, c, d) + x + t, s); }
constexpr ALWAYS_INLINE u32 CalculateRound2(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + G(b, c, d) + x + t, s); }
constexpr ALWAYS_INLINE u32 CalculateRound3(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + H(b, c, d) + x + t, s); }
constexpr ALWAYS_INLINE u32 CalculateRound4(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + I(b, c, d) + x + t, s); }
void Encode(u32 *dst, const u32 *src, size_t size) {
if constexpr (util::IsBigEndian()) {
for (size_t i = 0; i < size; i += sizeof(u32)) {
util::StoreLittleEndian(dst + i, src[i]);
}
} else {
std::memcpy(dst, src, size);
}
}
void Decode(u32 *dst, const u32 *src, size_t size) {
if constexpr (util::IsBigEndian()) {
for (size_t i = 0; i < size; i += sizeof(u32)) {
dst[i] = util::LoadLittleEndian(src + i);
}
} else {
std::memcpy(dst, src, size);
}
}
}
void Md5Impl::Initialize() {
/* Set constants. */
m_x.p.a = Md5Constants::A;
m_x.p.b = Md5Constants::B;
m_x.p.c = Md5Constants::C;
m_x.p.d = Md5Constants::D;
/* Set size. */
m_size = 0;
/* Set initialized. */
m_state = State_Initialized;
}
void Md5Impl::Update(const void *data, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(m_state == State_Initialized);
/* Determine how much we can process. */
const size_t work_idx = m_size % BlockSize;
const size_t work_remaining = BlockSize - work_idx;
/* Increment our size. */
m_size += size;
/* Copy in the data to our buffer, if we don't have a full block. */
if (work_remaining > size) {
if (size > 0) {
std::memcpy(m_y + work_idx, data, size);
}
return;
}
/* Copy what we can to complete our block. */
std::memcpy(m_y + work_idx, data, work_remaining);
/* Process the block. */
this->ProcessBlock();
/* Adjust size to account for what we've processed. */
size -= work_remaining;
/* Process as many full blocks as we can. */
const u8 *cur_block = static_cast<const u8 *>(data) + work_remaining;
for (size_t i = 0; i < size / BlockSize; ++i) {
std::memcpy(m_y, cur_block, BlockSize);
cur_block += BlockSize;
this->ProcessBlock();
}
/* Copy in any leftover data. */
if (const auto left = size % BlockSize; left > 0) {
std::memcpy(m_y, cur_block, left);
}
}
void Md5Impl::GetHash(void *dst, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
AMS_ASSERT(size >= HashSize);
AMS_UNUSED(size);
/* If we need to, finish processing. */
if (m_state == State_Initialized) {
this->ProcessLastBlock();
m_state = State_Done;
}
/* Encode the result. */
Encode(static_cast<u32 *>(dst), m_x.state, HashSize);
}
void Md5Impl::ProcessBlock() {
/* Declare tracking pointers for rounds. */
u32 x[BlockSize / sizeof(u32)];
const u32 *p_t = Md5Constants::T;
const u32 *p_k = Md5Constants::K;
const u32 *p_x = x;
/* Extract current state. */
u32 a = m_x.p.a;
u32 b = m_x.p.b;
u32 c = m_x.p.c;
u32 d = m_x.p.d;
/* Decode the block into native endian. */
Decode(x, reinterpret_cast<const u32 *>(m_y), BlockSize);
/* Perform round 1. */
for (size_t i = 0; i < 4; ++i) {
a = CalculateRound1(a, b, c, d, *p_x++, 7, *p_t++);
d = CalculateRound1(d, a, b, c, *p_x++, 12, *p_t++);
c = CalculateRound1(c, d, a, b, *p_x++, 17, *p_t++);
b = CalculateRound1(b, c, d, a, *p_x++, 22, *p_t++);
}
/* Perform round 2. */
for (size_t i = 0; i < 4; ++i) {
a = CalculateRound2(a, b, c, d, x[*p_k++], 5, *p_t++);
d = CalculateRound2(d, a, b, c, x[*p_k++], 9, *p_t++);
c = CalculateRound2(c, d, a, b, x[*p_k++], 14, *p_t++);
b = CalculateRound2(b, c, d, a, x[*p_k++], 20, *p_t++);
}
/* Perform round 3. */
for (size_t i = 0; i < 4; ++i) {
a = CalculateRound3(a, b, c, d, x[*p_k++], 4, *p_t++);
d = CalculateRound3(d, a, b, c, x[*p_k++], 11, *p_t++);
c = CalculateRound3(c, d, a, b, x[*p_k++], 16, *p_t++);
b = CalculateRound3(b, c, d, a, x[*p_k++], 23, *p_t++);
}
/* Perform round 4. */
for (size_t i = 0; i < 4; ++i) {
a = CalculateRound4(a, b, c, d, x[*p_k++], 6, *p_t++);
d = CalculateRound4(d, a, b, c, x[*p_k++], 10, *p_t++);
c = CalculateRound4(c, d, a, b, x[*p_k++], 15, *p_t++);
b = CalculateRound4(b, c, d, a, x[*p_k++], 21, *p_t++);
}
/* Mix the result back into our state. */
m_x.p.a += a;
m_x.p.b += b;
m_x.p.c += c;
m_x.p.d += d;
}
void Md5Impl::ProcessLastBlock() {
/* Get bit count. */
const u64 bit_count = m_size * BITSIZEOF(u8);
/* Add padding byte unconditionally. */
this->Update(Md5Constants::Padding, sizeof(Md5Constants::Padding));
/* Determine remaining. */
size_t work_idx = m_size % BlockSize;
size_t work_remaining = BlockSize - work_idx;
/* We want to process 8000.....{bit count}. */
if (work_remaining < sizeof(u64)) {
std::memset(m_y + work_idx, 0, work_remaining);
this->ProcessBlock();
work_idx = 0;
work_remaining = BlockSize;
}
if (work_remaining > sizeof(u64)) {
std::memset(m_y + work_idx, 0, work_remaining - sizeof(u64));
}
util::StoreLittleEndian<u64>(reinterpret_cast<u64 *>(m_y + BlockSize - sizeof(u64)), bit_count);
this->ProcessBlock();
}
}

View File

@@ -49,10 +49,9 @@ namespace ams::kern::init {
constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); constexpr PageTableEntry KernelRwDataUncachedAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemoryNotCacheable, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped);
void StoreDataCache(const void *addr, size_t size) { void StoreDataCache(const void *addr, size_t size) {
uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize); const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), cpu::DataCacheLineSize);
uintptr_t end = reinterpret_cast<uintptr_t>(addr) + size; for (size_t stored = 0; stored < size; stored += cpu::DataCacheLineSize) {
for (uintptr_t cur = start; cur < end; cur += cpu::DataCacheLineSize) { __asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(start + stored) : "memory");
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(cur) : "memory");
} }
cpu::DataSynchronizationBarrier(); cpu::DataSynchronizationBarrier();
} }
@@ -594,11 +593,13 @@ namespace ams::kern::init {
switch (num_watchpoints) { switch (num_watchpoints) {
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0) FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_WATCHPOINT_CASE, 0)
case 0:
cpu::SetDbgWcr0El1(0);
cpu::SetDbgWvr0El1(0);
[[fallthrough]];
default: default:
break; break;
} }
cpu::SetDbgWcr0El1(0);
cpu::SetDbgWvr0El1(0);
switch (num_breakpoints) { switch (num_breakpoints) {
FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0) FOR_I_IN_15_TO_1(MESOSPHERE_INITIALIZE_BREAKPOINT_CASE, 0)

View File

@@ -376,7 +376,7 @@ _ZN3ams4kern4arch5arm6430EL1SynchronousExceptionHandlerEv:
stp x26, x27, [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)]
mrs x20, sp_el0 ldr x20, [sp]
mrs x21, elr_el1 mrs x21, elr_el1
mrs x22, spsr_el1 mrs x22, spsr_el1
mrs x23, tpidr_el0 mrs x23, tpidr_el0
@@ -529,7 +529,7 @@ _ZN3ams4kern4arch5arm6421EL1SystemErrorHandlerEv:
stp x26, x27, [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)]
mrs x20, sp_el0 ldr x20, [sp]
mrs x21, elr_el1 mrs x21, elr_el1
mrs x22, spsr_el1 mrs x22, spsr_el1
mrs x23, tpidr_el0 mrs x23, tpidr_el0

View File

@@ -126,7 +126,8 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
cmp x7, x18 cmp x7, x18
b.ne 1f b.ne 1f
/* If they're the same, then we can just return as there's nothing to do. */ /* If they're the same, then we can just issue a memory barrier and return. */
dmb ish
ret ret
0: /* The interrupt task thread is runnable. */ 0: /* The interrupt task thread is runnable. */
@@ -182,9 +183,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
/* Get the highest priority thread's context, and save it. */ /* Get the highest priority thread's context, and save it. */
/* ams::kern::KThread::GetContextForSchedulerLoop() */ /* ams::kern::KThread::GetContextForSchedulerLoop() */
mov x0, x21 add x22, x21, #(THREAD_THREAD_CONTEXT)
bl _ZN3ams4kern7KThread26GetContextForSchedulerLoopEv
mov x22, x0
/* Prepare to try to acquire the context lock. */ /* Prepare to try to acquire the context lock. */
add x1, x22, #(THREAD_CONTEXT_LOCKED) add x1, x22, #(THREAD_CONTEXT_LOCKED)
@@ -226,26 +225,7 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
mov x0, x22 mov x0, x22
RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f) RESTORE_THREAD_CONTEXT(x0, x1, x2, 9f)
9: /* Configure single-step, if we should. */ 9: /* We're done restoring the thread context, and can return safely. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Get a reference to the new thread's stack parameters. */
add x2, sp, #0x1000
and x2, x2, #~(0x1000-1)
/* Read the single-step flag. */
ldurb w2, [x2, #-(THREAD_STACK_PARAMETERS_SIZE - THREAD_STACK_PARAMETERS_IS_SINGLE_STEP)]
/* Update the single-step bit in mdscr_el1. */
mrs x1, mdscr_el1
bic x1, x1, #1
orr x1, x1, x2
msr mdscr_el1, x1
isb
#endif
/* We're done restoring the thread context, and can return safely. */
ret ret
10: /* Our switch failed. */ 10: /* Our switch failed. */