Compare commits

..

57 Commits

Author SHA1 Message Date
hexkyz
931e3c37fd docs: add changelog for 1.11.0 2026-04-03 23:34:13 +01:00
hexkyz
4b4db91341 pgl: no need to initialize lr services when already using our own 2026-04-03 23:32:15 +01:00
hexkyz
e3fc339fe5 Merge pull request #2766 from alula/erpt-crash-fix
erpt: Fix rare aborts caused by ResultInvalidPowerState
2026-04-03 23:09:55 +01:00
Alula
a051c3c723 erpt: document unknown 0x20000 flag 2026-04-04 00:06:49 +02:00
Alula
9a1ea02c8c erpt: Add some missed 20.0.0+ FsInfo changes 2026-04-04 00:06:49 +02:00
hexkyz
bae1ad22ec Merge pull request #2768 from masagrator/patch-1
Update services in PGL to match 21.0.0
2026-04-03 22:33:28 +01:00
hexkyz
cd1503a9ca emummc: fix offsets
22.0.0 now boots under emummc
2026-04-03 22:31:19 +01:00
Alula
72e3b0dd34 erpt: Fix rare aborts caused by ResultInvalidPowerState 2026-04-03 22:55:09 +02:00
MasaGratoR
ac120cf84b Match 21.0.0 FW 2026-04-03 22:23:08 +02:00
MasaGratoR
97ffad2ab6 Update service access to match 21.0.0 2026-04-03 22:22:15 +02:00
hexkyz
c8d68a3a8a Merge pull request #2763 from masagrator/patch-1
Fix off by 1 bug + allocation error + missing initialize in PGL
2026-04-03 19:27:56 +01:00
hexkyz
8987d642d1 Merge pull request #2748 from CTCaer/auto_dram_cfg
[Feature] Auto dram cfg & Auto memory mode
2026-04-03 19:27:38 +01:00
hexkyz
7ffb1da2ac Merge pull request #2734 from ndeadly/r_discard
libvapours: add R_DISCARD macro
2026-04-03 19:27:19 +01:00
hexkyz
5c426bf90d loader: remove unused file 2026-04-03 19:09:28 +01:00
MasaGratoR
31ca20f3b7 Add missing libstratosphere lr::Initialize() 2026-04-02 21:09:15 +02:00
MasaGratoR
174da786e4 Override new and delete operators 2026-04-02 20:45:30 +02:00
hexkyz
93a82c0441 loader: patch am to recover homebrew compatibility
This patch is to be removed if/once hbmenu/libnx re-designs the exiting logic
2026-04-02 01:42:53 +01:00
hexkyz
082115187a loader/util: fully implement zstd bic variant
Implement both compression and decompression utilities and simplify loader logic
2026-04-02 01:30:45 +01:00
hexkyz
db388385b0 Merge pull request #2761 from alula/22_support
22.0.0 support part 3
2026-04-02 01:17:19 +01:00
MasaGratoR
4a8f7628c2 Update pgl_srv_shell_host_utils.cpp 2026-04-01 20:44:08 +02:00
MasaGratoR
9d16ee6a74 Update pgl_srv_shell_host_utils.cpp 2026-04-01 20:41:25 +02:00
MasaGratoR
3b9ee08c69 Update pgl_srv_shell_host_utils.cpp 2026-04-01 20:33:27 +02:00
MasaGratoR
2694f31eb3 Update pgl_srv_shell_host_utils.cpp 2026-04-01 20:31:47 +02:00
MasaGratoR
f3f1fa46ed Fix off by 1 bug 2026-04-01 20:17:52 +02:00
Alula
8a7c2872c3 erpt: use official name for erpt::IManager cmd 7 2026-04-01 05:57:28 +02:00
Alula
6acdc05c89 erpt: revert GetReportSizeMax to not potentially break old firmwares 2026-04-01 05:46:04 +02:00
hexkyz
32bb6b17ab Merge pull request #2753 from alula/22_support
Basic ("the console boots and functions as expected") 22.0.0 support - Part 2
2026-04-01 03:10:19 +01:00
Alula
27455329b3 loader: update for 22.0.0 2026-03-28 01:43:19 +01:00
Alula
7d334f09ee loader: the two unknown titles are LGE/LGP for China 2026-03-27 23:57:44 +01:00
Alula
8c0ff851f2 loader/strat: Add amalgamated zstd 1.5.7 with ZBIC patches 2026-03-27 23:51:04 +01:00
hexkyz
b108318996 Merge pull request #2744 from alula/22_support
Basic ("the console boots and functions as expected") 22.0.0 support
2026-03-20 00:22:10 +00:00
hexkyz
5dfdc6e8b0 Merge emummc changes from pull request #2743
Remaining changes are duplicated in pull request #2744
2026-03-20 00:17:19 +00:00
hexkyz
14e63cb04c loader: add USB 3.0 patches for 22.0.0
Additionally remove duplicated entry for 20.1.0, just use the 20.0.0 one.
2026-03-20 00:02:42 +00:00
CTCaer
5dd9816e3c exosphere: allow memory mode to be used on retail 2026-03-19 16:35:36 +02:00
CTCaer
5a5d9b206b exosphere: automatically adjust dram id if needed
Checks if programmed memory size matches the one from fused dram id.
If not, adjust it properly so PCV can do proper training and not crash.
2026-03-19 16:32:12 +02:00
Alula
49e2330ed8 erpt: rename old ControllerStyleList field 2026-03-18 23:40:40 +01:00
Alula
1847db06f8 erpt: Implement 22.0.0 commands and changes
Co-authored-by: nvnprogram <97150065+nvnprogram@users.noreply.github.com>
2026-03-18 23:33:55 +01:00
Alula
f028802fb8 erpt: make FieldType u8 to match Nintendo 2026-03-18 23:30:06 +01:00
Alula
b29dbeae3d kern: don't allow mapping device pages as executable on abi 26.x+ 2026-03-18 23:25:24 +01:00
Alula
4e653f67e5 kern: make KProcess layout accurate to N's code 2026-03-18 23:25:24 +01:00
Alula
a985bdb1d4 kern: N made this an u32 for some reason 2026-03-18 23:25:24 +01:00
Alula
5b0a4830d4 pinmux: add a build time option to configure JC rail uart 2026-03-18 23:25:24 +01:00
Alula
26990b3be9 fs: DisableAutoSaveDataCreation was removed in 22 2026-03-18 23:25:24 +01:00
Alula
76bceffbd5 svc: bump supported kernel version 2026-03-18 23:25:24 +01:00
Alula
0ee6277be9 kern: write thread handle to tls +0x110 on creation 2026-03-18 23:25:24 +01:00
Alula
9cc82c6f80 fusee/exo/ams: update with new keydata/version enums 2026-03-18 23:25:24 +01:00
hexkyz
6b831406d6 erpt: style fix 2026-03-18 21:35:18 +00:00
hexkyz
23ebd4d677 erpt: add new IDs for 22.0.0 2026-03-18 21:32:35 +00:00
hexkyz
00f987dd38 erpt: types are now a byte in 22.0.0 2026-03-18 21:18:05 +00:00
ndeadly
f8a5a6c015 libvapours: add R_DISCARD macro 2026-02-23 01:09:11 +01:00
hexkyz
61ac03e22d Merge pull request #2703 from HydrationMan/master
Update fusee_display.cpp - add missing newline
2026-02-05 21:47:59 +00:00
hexkyz
208b60696c Merge pull request #2726 from ndeadly/svc-shim-compat
libstrat: update GetDebugEvent svc shim for compatibility with libnx changes
2026-02-05 21:37:17 +00:00
ndeadly
dfb936ed11 libstrat: update GetDebugEvent svc shim for compatibility with libnx changes 2026-02-03 22:59:47 +01:00
Michael Scire
5056ab21af git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "9a8703e71"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "9a8703e71"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2026-01-15 15:39:39 -07:00
Michael Scire
eb34f9789c git subrepo push emummc
subrepo:
  subdir:   "emummc"
  merged:   "8ab963b0b"
upstream:
  origin:   "https://github.com/m4xw/emummc"
  branch:   "develop"
  commit:   "8ab963b0b"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2026-01-13 23:25:52 -07:00
Michael Scire
1e88f37892 ams/emummc: update for 21.2.0 2026-01-13 23:24:53 -07:00
Kane
cd72f9f33e Update fusee_display.cpp - add missing newline
Add newline that would otherwise cause text concatenation when it comes to suggestedFix content being appended.
2026-01-01 23:16:38 +13:00
73 changed files with 57248 additions and 281 deletions

View File

@@ -15,6 +15,13 @@
# Desc: Controls whether userland has access to the PMU registers.
# NOTE: It is unknown what effects this has on official code.
# Key: enable_mem_mode, default: 0.
# Desc: Controls whether boot config memory mode is taken into account
# for retail units. This does not affect development units.
# NOTE: On retail units max ram size is capped to 4GB.
# Enabling this will use the boot config memory mode parameter,
# which by default is auto and size gets set based on physical size.
# Key: blank_prodinfo_sysmmc, default: 0.
# Desc: Controls whether PRODINFO should be blanked in sysmmc.
# This will cause the system to see dummied out keys and
@@ -51,6 +58,7 @@ debugmode=1
debugmode_user=0
disable_user_exception_handlers=0
enable_user_pmu_access=0
enable_mem_mode=0
blank_prodinfo_sysmmc=0
blank_prodinfo_emummc=0
allow_writing_to_cal_sysmmc=0

View File

@@ -1,4 +1,29 @@
# Changelog
## 1.11.0
+ Support was added for 22.0.0.
+ Special thanks to @alula for handling a large chunk of the necessary kernel, loader and erpt changes.
+ The console should boot and atmosphère should be fully functional.
+ **Please note**: A change in how applications/applets' lifespan is managed broke the homebrew ecosystem.
+ All applications and applets are expected to perform a clean exit by calling the relevant IPC commands.
+ Homebrew compiled with libnx and hbmenu itself explicitly avoid exiting so it can keep using the same process.
+ Properly fixing this would require a re-design of how homebrew is launched and terminated.
+ As a temporary solution, patches to the `am` sysmodule are now included which allow restoring the previous behavior and regain homebrew compatibility without any further changes.
+ Nonetheless, please report any issues you may face when testing homebrew to ensure no edge cases have been missed.
+ `exosphère` was updated to reflect the latest official secure monitor behavior.
+ `mesosphère` was updated to reflect the latest official kernel behavior.
+ `loader` was updated to reflect the latest official behavior.
+ `erpt` was updated to reflect the latest official behavior.
+ `pgl` was updated to fix a few issues.
+ Support was added for memory modes above 4GB (thanks @CTCaer).
+ A build time option to configure Joy-Con rails as UART for debugging was added (thanks @alula).
+ General system stability improvements to enhance the user's experience.
## 1.10.2
+ Basic support was added for 21.2.0.
+ General system stability improvements to enhance the user's experience.
## 1.10.1
+ Basic support was added for 21.1.0.
+ A bug was fixed that caused some games (e.g. Tomb Raider definitive edition) to fail to launch.
+ General system stability improvements to enhance the user's experience.
## 1.10.0
+ Basic support was added for 21.0.0.
+ The console should boot and atmosphère should be fully functional.

4
emummc/.gitrepo vendored
View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emummc
branch = develop
commit = 3c57b20ba3820ec87d7dd239d6fcf9ba97510606
parent = d61ee942d9b34cadd80464d5d549c5e2e5bf1689
commit = 8ab963b0b1c24b68de8e0c98c62c7822a9765bf3
parent = 1e88f37892555da4c38ca6c95f43c56cc6bb87e6
method = merge
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***
### Supported Horizon Versions
**1.0.0 - 21.0.0**
**1.0.0 - 21.2.0**
## Features
* Arbitrary SDMMC backend selection

View File

@@ -81,6 +81,10 @@
#include "offsets/2010_exfat.h"
#include "offsets/2100.h"
#include "offsets/2100_exfat.h"
#include "offsets/2120.h"
#include "offsets/2120_exfat.h"
#include "offsets/2200.h"
#include "offsets/2200_exfat.h"
#include "../utils/fatal.h"
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
@@ -173,6 +177,10 @@ DEFINE_OFFSET_STRUCT(_2010);
DEFINE_OFFSET_STRUCT(_2010_EXFAT);
DEFINE_OFFSET_STRUCT(_2100);
DEFINE_OFFSET_STRUCT(_2100_EXFAT);
DEFINE_OFFSET_STRUCT(_2120);
DEFINE_OFFSET_STRUCT(_2120_EXFAT);
DEFINE_OFFSET_STRUCT(_2200);
DEFINE_OFFSET_STRUCT(_2200_EXFAT);
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
switch (version) {
@@ -306,6 +314,14 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
return &(GET_OFFSET_STRUCT_NAME(_2100));
case FS_VER_21_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_2100_EXFAT));
case FS_VER_21_2_0:
return &(GET_OFFSET_STRUCT_NAME(_2120));
case FS_VER_21_2_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_2120_EXFAT));
case FS_VER_22_0_0:
return &(GET_OFFSET_STRUCT_NAME(_2200));
case FS_VER_22_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_2200_EXFAT));
default:
fatal_abort(Fatal_UnknownVersion);
}

View File

@@ -119,6 +119,12 @@ enum FS_VER
FS_VER_21_0_0,
FS_VER_21_0_0_EXFAT,
FS_VER_21_2_0,
FS_VER_21_2_0_EXFAT,
FS_VER_22_0_0,
FS_VER_22_0_0_EXFAT,
FS_VER_MAX,
};

59
emummc/source/FS/offsets/2120.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_2120_H__
#define __FS_2120_H__
// Accessor vtable getters
#define FS_OFFSET_2120_SDMMC_ACCESSOR_GC 0x1AC970
#define FS_OFFSET_2120_SDMMC_ACCESSOR_SD 0x1AE980
#define FS_OFFSET_2120_SDMMC_ACCESSOR_NAND 0x1ACFA0
// Hooks
#define FS_OFFSET_2120_SDMMC_WRAPPER_READ 0x1A8850
#define FS_OFFSET_2120_SDMMC_WRAPPER_WRITE 0x1A88B0
#define FS_OFFSET_2120_RTLD 0x2E1C0
#define FS_OFFSET_2120_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x4C)))
#define FS_OFFSET_2120_CLKRST_SET_MIN_V_CLK_RATE 0x1CB9B0
// Misc funcs
#define FS_OFFSET_2120_LOCK_MUTEX 0x1A17D0
#define FS_OFFSET_2120_UNLOCK_MUTEX 0x1A1830
#define FS_OFFSET_2120_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1A8810
#define FS_OFFSET_2120_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1A8830
// Misc Data
#define FS_OFFSET_2120_SD_MUTEX 0xFEE408
#define FS_OFFSET_2120_NAND_MUTEX 0xFE9CF0
#define FS_OFFSET_2120_ACTIVE_PARTITION 0xFE9D30
#define FS_OFFSET_2120_SDMMC_DAS_HANDLE 0xFCBB18
// NOPs
#define FS_OFFSET_2120_SD_DAS_INIT 0x2B5C8
// Nintendo Paths
#define FS_OFFSET_2120_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x000718CC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x000824F4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008AF18, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x000A0B8C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_2120_H__

59
emummc/source/FS/offsets/2120_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_2120_EXFAT_EXFAT_H__
#define __FS_2120_EXFAT_EXFAT_H__
// Accessor vtable getters
#define FS_OFFSET_2120_EXFAT_SDMMC_ACCESSOR_GC 0x1B7AD0
#define FS_OFFSET_2120_EXFAT_SDMMC_ACCESSOR_SD 0x1B9AE0
#define FS_OFFSET_2120_EXFAT_SDMMC_ACCESSOR_NAND 0x1B8100
// Hooks
#define FS_OFFSET_2120_EXFAT_SDMMC_WRAPPER_READ 0x1B39B0
#define FS_OFFSET_2120_EXFAT_SDMMC_WRAPPER_WRITE 0x1B3A10
#define FS_OFFSET_2120_EXFAT_RTLD 0x2E1C0
#define FS_OFFSET_2120_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x4C)))
#define FS_OFFSET_2120_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1D6B10
// Misc funcs
#define FS_OFFSET_2120_EXFAT_LOCK_MUTEX 0x1AC930
#define FS_OFFSET_2120_EXFAT_UNLOCK_MUTEX 0x1AC990
#define FS_OFFSET_2120_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1B3970
#define FS_OFFSET_2120_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1B3990
// Misc Data
#define FS_OFFSET_2120_EXFAT_SD_MUTEX 0xFFF408
#define FS_OFFSET_2120_EXFAT_NAND_MUTEX 0xFFACF0
#define FS_OFFSET_2120_EXFAT_ACTIVE_PARTITION 0xFFAD30
#define FS_OFFSET_2120_EXFAT_SDMMC_DAS_HANDLE 0xFD8B18
// NOPs
#define FS_OFFSET_2120_EXFAT_SD_DAS_INIT 0x2B5C8
// Nintendo Paths
#define FS_OFFSET_2120_EXFAT_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x000718CC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x000824F4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008AF18, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x000A0B8C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_2120_EXFAT_EXFAT_H__

59
emummc/source/FS/offsets/2200.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_2200_H__
#define __FS_2200_H__
// Accessor vtable getters
#define FS_OFFSET_2200_SDMMC_ACCESSOR_GC 0x1B01C0
#define FS_OFFSET_2200_SDMMC_ACCESSOR_SD 0x1B21C0
#define FS_OFFSET_2200_SDMMC_ACCESSOR_NAND 0x1B07F0
// Hooks
#define FS_OFFSET_2200_SDMMC_WRAPPER_READ 0x1AC0F0
#define FS_OFFSET_2200_SDMMC_WRAPPER_WRITE 0x1AC150
#define FS_OFFSET_2200_RTLD 0x2DED0
#define FS_OFFSET_2200_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x4C)))
#define FS_OFFSET_2200_CLKRST_SET_MIN_V_CLK_RATE 0x1CF260
// Misc funcs
#define FS_OFFSET_2200_LOCK_MUTEX 0x1A4EF0
#define FS_OFFSET_2200_UNLOCK_MUTEX 0x1A4F40
#define FS_OFFSET_2200_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1AC0B0
#define FS_OFFSET_2200_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1AC0D0
// Misc Data
#define FS_OFFSET_2200_SD_MUTEX 0xFF1408
#define FS_OFFSET_2200_NAND_MUTEX 0xFECD00
#define FS_OFFSET_2200_ACTIVE_PARTITION 0xFECD40
#define FS_OFFSET_2200_SDMMC_DAS_HANDLE 0xFCEB98
// NOPs
#define FS_OFFSET_2200_SD_DAS_INIT 0x2B438
// Nintendo Paths
#define FS_OFFSET_2200_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x000715EC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00082E64, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008B888, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x000A1B1C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_2200_H__

59
emummc/source/FS/offsets/2200_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_2200_EXFAT_H__
#define __FS_2200_EXFAT_H__
// Accessor vtable getters
#define FS_OFFSET_2200_EXFAT_SDMMC_ACCESSOR_GC 0x1BB3B0
#define FS_OFFSET_2200_EXFAT_SDMMC_ACCESSOR_SD 0x1BD3B0
#define FS_OFFSET_2200_EXFAT_SDMMC_ACCESSOR_NAND 0x1BB9E0
// Hooks
#define FS_OFFSET_2200_EXFAT_SDMMC_WRAPPER_READ 0x1B72E0
#define FS_OFFSET_2200_EXFAT_SDMMC_WRAPPER_WRITE 0x1B7340
#define FS_OFFSET_2200_EXFAT_RTLD 0x2DED0
#define FS_OFFSET_2200_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x4C)))
#define FS_OFFSET_2200_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1DA450
// Misc funcs
#define FS_OFFSET_2200_EXFAT_LOCK_MUTEX 0x1B00E0
#define FS_OFFSET_2200_EXFAT_UNLOCK_MUTEX 0x1B0130
#define FS_OFFSET_2200_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1B72A0
#define FS_OFFSET_2200_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1B72C0
// Misc Data
#define FS_OFFSET_2200_EXFAT_SD_MUTEX 0x1002408
#define FS_OFFSET_2200_EXFAT_NAND_MUTEX 0xFFDD00
#define FS_OFFSET_2200_EXFAT_ACTIVE_PARTITION 0xFFDD40
#define FS_OFFSET_2200_EXFAT_SDMMC_DAS_HANDLE 0xFDBB98
// NOPs
#define FS_OFFSET_2200_EXFAT_SD_DAS_INIT 0x2B438
// Nintendo Paths
#define FS_OFFSET_2200_EXFAT_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x000715EC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00082E64, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008B888, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x000A1B1C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_2200_EXFAT_H__

View File

@@ -85,10 +85,10 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
/* We can get away with only including latest because exosphere supports newer-than-expected master key in engine. */
/* TODO: Update on next change of keys. */
/* Mariko Development Master Kek Source. */
.byte 0x11, 0x1C, 0x13, 0x90, 0xD9, 0x5E, 0xB0, 0xA2, 0xE3, 0xD0, 0x0E, 0x5D, 0xC4, 0xFB, 0x9E, 0xDB
.byte 0x2E, 0x27, 0x44, 0xEA, 0x32, 0xF8, 0x2C, 0xF0, 0x6F, 0xCA, 0xCD, 0x77, 0xAE, 0xAE, 0x1A, 0x1B
/* Mariko Production Master Kek Source. */
.byte 0xEB, 0xF3, 0x5B, 0x2D, 0x4A, 0x2D, 0xCE, 0x45, 0x3A, 0x6F, 0x61, 0x38, 0x0B, 0x00, 0x3B, 0x46
.byte 0x82, 0xE2, 0x0A, 0x59, 0x67, 0xDF, 0xBF, 0x51, 0x47, 0x62, 0x11, 0xF2, 0x41, 0xD3, 0xEE, 0x13
/* Development Master Key Vectors. */
.byte 0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE /* Zeroes encrypted with Master Key 00. */
@@ -112,6 +112,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
.byte 0x90, 0x64, 0xF9, 0x08, 0x29, 0x88, 0xD4, 0xDC, 0x73, 0xA4, 0xA1, 0x13, 0x9E, 0x59, 0x85, 0xA0 /* Master key 11 encrypted with Master key 12. */
.byte 0x94, 0x46, 0x3B, 0xFA, 0x7D, 0xB9, 0xE2, 0x94, 0xC2, 0x9D, 0xB9, 0xA4, 0xB2, 0x56, 0xCA, 0xFE /* Master key 12 encrypted with Master key 13. */
.byte 0x74, 0xB2, 0x5F, 0xA0, 0x4B, 0x74, 0x6D, 0x47, 0x5B, 0xA9, 0xF5, 0x26, 0x46, 0xD7, 0x4B, 0x6E /* Master key 13 encrypted with Master key 14. */
.byte 0x97, 0xB3, 0x61, 0x88, 0x5C, 0x0D, 0xA1, 0x38, 0x73, 0xA4, 0x2F, 0x1A, 0x46, 0xA1, 0x09, 0xBF /* Master key 14 encrypted with Master key 15. */
/* Production Master Key Vectors. */
.byte 0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D /* Zeroes encrypted with Master Key 00. */
@@ -135,6 +136,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
.byte 0x4A, 0x01, 0x3B, 0xC7, 0x44, 0x6E, 0x45, 0xBD, 0xE6, 0x5E, 0x2B, 0xEC, 0x07, 0x37, 0x52, 0x86 /* Master key 11 encrypted with Master key 12. */
.byte 0x97, 0xE4, 0x11, 0xAB, 0x22, 0x72, 0x1A, 0x1F, 0x70, 0x5C, 0x00, 0xB3, 0x96, 0x30, 0x05, 0x28 /* Master key 12 encrypted with Master key 13. */
.byte 0xF7, 0x92, 0xC0, 0xEC, 0xF3, 0xA4, 0x8C, 0xB7, 0x0D, 0xB3, 0xF3, 0xAB, 0x10, 0x9B, 0x18, 0xBA /* Master key 13 encrypted with Master key 14. */
.byte 0x14, 0xCB, 0x60, 0x29, 0x3D, 0xE0, 0xFB, 0xF2, 0x5B, 0x60, 0xB6, 0xC5, 0x2E, 0x77, 0x8F, 0x98 /* Master key 14 encrypted with Master key 15. */
/* Device Master Key Source Sources. */
.byte 0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D /* 4.0.0 Device Master Key Source Source. */
@@ -155,6 +157,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
.byte 0x07, 0x38, 0x9A, 0xEC, 0x9C, 0xBD, 0x50, 0x4A, 0x4C, 0x1F, 0x04, 0xDA, 0x40, 0x68, 0x29, 0xE3 /* 19.0.0 Device Master Key Source Source. */
.byte 0xA3, 0x6B, 0x0A, 0xB5, 0x6F, 0x57, 0x4C, 0x5E, 0x00, 0xFD, 0x56, 0x21, 0xF5, 0x06, 0x6B, 0xD1 /* 20.0.0 Device Master Key Source Source. */
.byte 0xF9, 0x62, 0x05, 0x99, 0xE0, 0xB9, 0xA6, 0x9B, 0x9D, 0xAA, 0xB4, 0x12, 0x0B, 0x0F, 0xF5, 0x8F /* 21.0.0 Device Master Key Source Source. */
.byte 0xF8, 0xF4, 0x22, 0xA4, 0x34, 0xAE, 0x0E, 0x0C, 0x4D, 0x5C, 0x5B, 0xA1, 0x1B, 0x46, 0x1C, 0x78 /* 22.0.0 Device Master Key Source Source. */
/* Development Device Master Kek Sources. */
.byte 0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34 /* 4.0.0 Device Master Kek Source. */
@@ -175,6 +178,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
.byte 0xAE, 0x78, 0x36, 0xB6, 0x91, 0xEB, 0xAF, 0x9C, 0x18, 0xF1, 0xC0, 0xD5, 0x8A, 0x0C, 0x7C, 0xA1 /* 19.0.0 Device Master Kek Source. */
.byte 0x09, 0x12, 0x4F, 0x26, 0x90, 0xB9, 0xA6, 0xF5, 0xA5, 0x18, 0x74, 0xB6, 0x8D, 0x80, 0x59, 0x3D /* 20.0.0 Device Master Kek Source. */
.byte 0x7A, 0x4C, 0x38, 0xB7, 0x03, 0x6B, 0x1E, 0x81, 0x20, 0x53, 0x14, 0x99, 0xA4, 0x21, 0x92, 0x9F /* 21.0.0 Device Master Kek Source. */
.byte 0xF3, 0xBC, 0xB5, 0xB5, 0x5F, 0x01, 0x50, 0x2B, 0x69, 0x69, 0x3A, 0x6B, 0xF9, 0x2C, 0x11, 0x9F /* 22.0.0 Device Master Kek Source. */
/* Production Device Master Kek Sources. */
.byte 0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D /* 4.0.0 Device Master Kek Source. */
@@ -194,4 +198,5 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
.byte 0xE7, 0x85, 0x8C, 0xA2, 0xF4, 0x49, 0xCB, 0x07, 0xD1, 0x8E, 0x48, 0x1B, 0xE8, 0x1E, 0x28, 0x3B /* 18.0.0 Device Master Kek Source. */
.byte 0x9B, 0xA5, 0xFD, 0x74, 0x7F, 0xCD, 0x23, 0xD1, 0xD9, 0xBD, 0x6C, 0x51, 0x72, 0x5F, 0x3D, 0x1F /* 19.0.0 Device Master Kek Source. */
.byte 0xDA, 0xFB, 0x61, 0x39, 0x48, 0x2D, 0xC2, 0x7E, 0x0D, 0x8E, 0x8F, 0x98, 0x57, 0x20, 0xB8, 0x15 /* 20.0.0 Device Master Kek Source. */
.byte 0x92, 0xBF, 0x37, 0x80, 0x0E, 0x79, 0x56, 0x8C, 0x57, 0x75, 0x72, 0x0A, 0x48, 0xD8, 0x15, 0x39 /* 21.0.0 Device Master Kek Source. */
.byte 0x92, 0xBF, 0x37, 0x80, 0x0E, 0x79, 0x56, 0x8C, 0x57, 0x75, 0x72, 0x0A, 0x48, 0xD8, 0x15, 0x39 /* 21.0.0 Device Master Kek Source. */
.byte 0xC4, 0x6F, 0x0E, 0x72, 0x43, 0xCE, 0x87, 0xFC, 0x38, 0x95, 0x9B, 0xC9, 0x31, 0x44, 0x97, 0x63 /* 22.0.0 Device Master Kek Source. */

View File

@@ -94,7 +94,7 @@ namespace ams::secmon::boot {
}
/* Check that the key generation is one that we can use. */
static_assert(pkg1::KeyGeneration_Count == 21);
static_assert(pkg1::KeyGeneration_Count == 22);
if (key_generation >= pkg1::KeyGeneration_Count) {
return false;
}

View File

@@ -132,10 +132,13 @@ namespace ams::secmon::smc {
}
u32 GetMemoryMode() {
/* Unless development function is enabled, we're 4 GB. */
/* Unless development function or forced boot config memory size is enabled, we're 4 GB. */
u32 memory_mode = pkg1::MemoryMode_4GB;
if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) {
const auto &bcd = GetBootConfig().data;
const auto &sc = GetSecmonConfiguration(); /* Exosphere extensions */
if (bcd.IsDevelopmentFunctionEnabled() || sc.IsBootConfigMemoryModeEnabled()) {
memory_mode = GetMemoryMode(bcd.GetMemoryMode());
}
@@ -146,17 +149,19 @@ namespace ams::secmon::smc {
pkg1::MemorySize memory_size = pkg1::MemorySize_4GB;
util::BitPack32 value = {};
if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) {
memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode()));
const auto &bcd = GetBootConfig().data;
const auto &sc = GetSecmonConfiguration(); /* Exosphere extensions */
if (bcd.IsDevelopmentFunctionEnabled()) {
value.Set<KernelConfiguration::Flags1>(bcd.GetKernelFlags1());
value.Set<KernelConfiguration::Flags0>(bcd.GetKernelFlags0());
}
value.Set<KernelConfiguration::PhysicalMemorySize>(memory_size);
if (bcd.IsDevelopmentFunctionEnabled() || sc.IsBootConfigMemoryModeEnabled()) {
memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode()));
}
/* Exosphere extensions. */
const auto &sc = GetSecmonConfiguration();
value.Set<KernelConfiguration::PhysicalMemorySize>(memory_size);
if (!sc.DisableUserModeExceptionHandlers()) {
value.Set<KernelConfiguration::EnableUserExceptionHandlers>(true);
@@ -169,6 +174,27 @@ namespace ams::secmon::smc {
return value.value;
}
fuse::DramId GetDramIdAdjusted() {
const auto dram_id = fuse::GetDramId();
AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count);
const auto fuse_mem_size = DramIdToMemorySize[dram_id];
const auto phys_mem_size = GetPhysicalMemorySize();
AMS_ABORT_UNLESS(fuse_mem_size <= phys_mem_size);
if (fuse_mem_size == phys_mem_size) {
return dram_id;
}
/* Adjust Dram ID to match density/ranks. */
if (GetSocType() == fuse::SocType_Erista) {
return fuse::DramId_IcosaSamsung6GB;
} else { /* fuse::SocType_Mariko */
return fuse::DramId_IowaSamsung1y8GBX;
}
}
constinit u64 g_payload_address = 0;
constinit bool g_set_true_target_firmware = false;
@@ -178,7 +204,7 @@ namespace ams::secmon::smc {
args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled();
break;
case ConfigItem::DramId:
args.r[1] = fuse::GetDramId();
args.r[1] = GetDramIdAdjusted(); /* Nintendo: fuse::GetDramId() */
break;
case ConfigItem::SecurityEngineInterruptNumber:
args.r[1] = SecurityEngineUserInterruptId;
@@ -471,9 +497,18 @@ namespace ams::secmon::smc {
/* For exosphere's usage. */
pkg1::MemorySize GetPhysicalMemorySize() {
const auto dram_id = fuse::GetDramId();
AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count);
return DramIdToMemorySize[dram_id];
const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress();
const u32 mem_size = reg::Read(MC + MC_EMEM_CFG) & 0x3FFF;
switch (mem_size >> 10) {
case 4:
default:
return pkg1::MemorySize_4GB;
case 6:
return pkg1::MemorySize_6GB;
case 8:
return pkg1::MemorySize_8GB;
}
}
}

View File

@@ -602,7 +602,7 @@ namespace ams::nxboot {
Print("\n");
if (R_SUCCEEDED(save_result)) {
Print("Report saved to /atmosphere/fatal_errors/report_%016" PRIx64 ".bin", f_ctx->report_identifier);
Print("Report saved to /atmosphere/fatal_errors/report_%016" PRIx64 ".bin\n", f_ctx->report_identifier);
} else {
Print("Failed to save report to the SD card! (%08" PRIx32 ")\n", save_result.GetValue());
}

View File

@@ -23,17 +23,17 @@ namespace ams::nxboot {
alignas(se::AesBlockSize) constexpr inline const u8 MarikoMasterKekSource[se::AesBlockSize] = {
/* TODO: Update on next change of keys. */
0xEB, 0xF3, 0x5B, 0x2D, 0x4A, 0x2D, 0xCE, 0x45, 0x3A, 0x6F, 0x61, 0x38, 0x0B, 0x00, 0x3B, 0x46
0x82, 0xE2, 0x0A, 0x59, 0x67, 0xDF, 0xBF, 0x51, 0x47, 0x62, 0x11, 0xF2, 0x41, 0xD3, 0xEE, 0x13
};
alignas(se::AesBlockSize) constexpr inline const u8 MarikoMasterKekSourceDev[se::AesBlockSize] = {
/* TODO: Update on next change of keys. */
0x11, 0x1C, 0x13, 0x90, 0xD9, 0x5E, 0xB0, 0xA2, 0xE3, 0xD0, 0x0E, 0x5D, 0xC4, 0xFB, 0x9E, 0xDB
0x2E, 0x27, 0x44, 0xEA, 0x32, 0xF8, 0x2C, 0xF0, 0x6F, 0xCA, 0xCD, 0x77, 0xAE, 0xAE, 0x1A, 0x1B
};
alignas(se::AesBlockSize) constexpr inline const u8 EristaMasterKekSource[se::AesBlockSize] = {
/* TODO: Update on next change of keys. */
0x66, 0xC8, 0xCB, 0x3D, 0xEC, 0xF4, 0x59, 0x73, 0x54, 0x88, 0xE1, 0x2E, 0xE6, 0x3D, 0x68, 0x46
0x15, 0xAC, 0x96, 0x34, 0xF5, 0x32, 0x56, 0x68, 0xFE, 0x5B, 0x9D, 0xD7, 0xED, 0x19, 0xB7, 0x8E
};
alignas(se::AesBlockSize) constexpr inline const u8 KeyblobKeySource[se::AesBlockSize] = {
@@ -75,6 +75,7 @@ namespace ams::nxboot {
{ 0x07, 0x38, 0x9A, 0xEC, 0x9C, 0xBD, 0x50, 0x4A, 0x4C, 0x1F, 0x04, 0xDA, 0x40, 0x68, 0x29, 0xE3 }, /* 19.0.0 Device Master Key Source Source. */
{ 0xA3, 0x6B, 0x0A, 0xB5, 0x6F, 0x57, 0x4C, 0x5E, 0x00, 0xFD, 0x56, 0x21, 0xF5, 0x06, 0x6B, 0xD1 }, /* 20.0.0 Device Master Key Source Source. */
{ 0xF9, 0x62, 0x05, 0x99, 0xE0, 0xB9, 0xA6, 0x9B, 0x9D, 0xAA, 0xB4, 0x12, 0x0B, 0x0F, 0xF5, 0x8F }, /* 21.0.0 Device Master Key Source Source. */
{ 0xF8, 0xF4, 0x22, 0xA4, 0x34, 0xAE, 0x0E, 0x0C, 0x4D, 0x5C, 0x5B, 0xA1, 0x1B, 0x46, 0x1C, 0x78 }, /* 22.0.0 Device Master Key Source Source. */
};
alignas(se::AesBlockSize) constexpr inline const u8 DeviceMasterKekSources[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
@@ -96,6 +97,7 @@ namespace ams::nxboot {
{ 0x9B, 0xA5, 0xFD, 0x74, 0x7F, 0xCD, 0x23, 0xD1, 0xD9, 0xBD, 0x6C, 0x51, 0x72, 0x5F, 0x3D, 0x1F }, /* 19.0.0 Device Master Kek Source. */
{ 0xDA, 0xFB, 0x61, 0x39, 0x48, 0x2D, 0xC2, 0x7E, 0x0D, 0x8E, 0x8F, 0x98, 0x57, 0x20, 0xB8, 0x15 }, /* 20.0.0 Device Master Kek Source. */
{ 0x92, 0xBF, 0x37, 0x80, 0x0E, 0x79, 0x56, 0x8C, 0x57, 0x75, 0x72, 0x0A, 0x48, 0xD8, 0x15, 0x39 }, /* 21.0.0 Device Master Kek Source. */
{ 0xC4, 0x6F, 0x0E, 0x72, 0x43, 0xCE, 0x87, 0xFC, 0x38, 0x95, 0x9B, 0xC9, 0x31, 0x44, 0x97, 0x63 }, /* 22.0.0 Device Master Kek Source. */
};
alignas(se::AesBlockSize) constexpr inline const u8 DeviceMasterKekSourcesDev[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
@@ -117,6 +119,7 @@ namespace ams::nxboot {
{ 0xAE, 0x78, 0x36, 0xB6, 0x91, 0xEB, 0xAF, 0x9C, 0x18, 0xF1, 0xC0, 0xD5, 0x8A, 0x0C, 0x7C, 0xA1 }, /* 19.0.0 Device Master Kek Source. */
{ 0x09, 0x12, 0x4F, 0x26, 0x90, 0xB9, 0xA6, 0xF5, 0xA5, 0x18, 0x74, 0xB6, 0x8D, 0x80, 0x59, 0x3D }, /* 20.0.0 Device Master Kek Source. */
{ 0x7A, 0x4C, 0x38, 0xB7, 0x03, 0x6B, 0x1E, 0x81, 0x20, 0x53, 0x14, 0x99, 0xA4, 0x21, 0x92, 0x9F }, /* 21.0.0 Device Master Kek Source. */
{ 0xF3, 0xBC, 0xB5, 0xB5, 0x5F, 0x01, 0x50, 0x2B, 0x69, 0x69, 0x3A, 0x6B, 0xF9, 0x2C, 0x11, 0x9F }, /* 22.0.0 Device Master Kek Source. */
};
alignas(se::AesBlockSize) constexpr inline const u8 MasterKeySources[pkg1::KeyGeneration_Count][se::AesBlockSize] = {
@@ -141,6 +144,7 @@ namespace ams::nxboot {
{ 0x4A, 0x01, 0x3B, 0xC7, 0x44, 0x6E, 0x45, 0xBD, 0xE6, 0x5E, 0x2B, 0xEC, 0x07, 0x37, 0x52, 0x86 }, /* Master key 11 encrypted with Master key 12. */
{ 0x97, 0xE4, 0x11, 0xAB, 0x22, 0x72, 0x1A, 0x1F, 0x70, 0x5C, 0x00, 0xB3, 0x96, 0x30, 0x05, 0x28 }, /* Master key 12 encrypted with Master key 13. */
{ 0xF7, 0x92, 0xC0, 0xEC, 0xF3, 0xA4, 0x8C, 0xB7, 0x0D, 0xB3, 0xF3, 0xAB, 0x10, 0x9B, 0x18, 0xBA }, /* Master key 13 encrypted with Master key 14. */
{ 0x14, 0xCB, 0x60, 0x29, 0x3D, 0xE0, 0xFB, 0xF2, 0x5B, 0x60, 0xB6, 0xC5, 0x2E, 0x77, 0x8F, 0x98 }, /* Master key 14 encrypted with Master key 15. */
};
alignas(se::AesBlockSize) constexpr inline const u8 MasterKeySourcesDev[pkg1::KeyGeneration_Count][se::AesBlockSize] = {
@@ -165,6 +169,7 @@ namespace ams::nxboot {
{ 0x90, 0x64, 0xF9, 0x08, 0x29, 0x88, 0xD4, 0xDC, 0x73, 0xA4, 0xA1, 0x13, 0x9E, 0x59, 0x85, 0xA0 }, /* Master key 11 encrypted with Master key 12. */
{ 0x94, 0x46, 0x3B, 0xFA, 0x7D, 0xB9, 0xE2, 0x94, 0xC2, 0x9D, 0xB9, 0xA4, 0xB2, 0x56, 0xCA, 0xFE }, /* Master key 12 encrypted with Master key 13. */
{ 0x74, 0xB2, 0x5F, 0xA0, 0x4B, 0x74, 0x6D, 0x47, 0x5B, 0xA9, 0xF5, 0x26, 0x46, 0xD7, 0x4B, 0x6E }, /* Master key 13 encrypted with Master key 14. */
{ 0x97, 0xB3, 0x61, 0x88, 0x5C, 0x0D, 0xA1, 0x38, 0x73, 0xA4, 0x2F, 0x1A, 0x46, 0xA1, 0x09, 0xBF }, /* Master key 14 encrypted with Master key 15. */
};
alignas(se::AesBlockSize) constinit u8 MasterKeys[pkg1::OldMasterKeyCount][se::AesBlockSize] = {};

View File

@@ -80,7 +80,7 @@ namespace ams::nxboot {
}
/* Check that the key generation is one that we can use. */
static_assert(pkg1::KeyGeneration_Count == 21);
static_assert(pkg1::KeyGeneration_Count == 22);
if (key_generation >= pkg1::KeyGeneration_Count) {
return false;
}

View File

@@ -267,6 +267,8 @@ namespace ams::nxboot {
return ams::TargetFirmware_20_0_0;
} else if (std::memcmp(package1 + 0x10, "20251009", 8) == 0) {
return ams::TargetFirmware_21_0_0;
} else if (std::memcmp(package1 + 0x10, "20260123", 8) == 0) {
return ams::TargetFirmware_22_0_0;
}
break;
default:
@@ -543,6 +545,12 @@ namespace ams::nxboot {
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess;
}
} else if (std::strcmp(entry.key, "enable_mem_mode") == 0) {
if (entry.value[0] == '1') {
storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled;
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled;
}
} else if (std::strcmp(entry.key, "blank_prodinfo_sysmmc") == 0) {
if (!emummc_enabled) {
if (entry.value[0] == '1') {

View File

@@ -189,6 +189,12 @@ namespace ams::nxboot {
FsVersion_21_0_0,
FsVersion_21_0_0_Exfat,
FsVersion_21_2_0,
FsVersion_21_2_0_Exfat,
FsVersion_22_0_0,
FsVersion_22_0_0_Exfat,
FsVersion_Count,
};
@@ -290,6 +296,12 @@ namespace ams::nxboot {
{ 0xEE, 0x4B, 0x30, 0x12, 0xA6, 0x84, 0x02, 0x25 }, /* FsVersion_21_0_0 */
{ 0x6E, 0x2B, 0xD9, 0xBA, 0xA3, 0xB9, 0x10, 0xF1 }, /* FsVersion_21_0_0_Exfat */
{ 0xAF, 0x1D, 0xBD, 0xC7, 0x82, 0x98, 0x3C, 0xBD }, /* FsVersion_21_2_0 */
{ 0x56, 0x25, 0x17, 0xA1, 0x92, 0xC3, 0xC8, 0xF0 }, /* FsVersion_21_2_0_Exfat */
{ 0xB7, 0xA2, 0x97, 0x39, 0xB7, 0xED, 0xDE, 0xFC }, /* FsVersion_22_0_0 */
{ 0xFB, 0x0B, 0x68, 0xDB, 0x24, 0x03, 0xD1, 0x19 }, /* FsVersion_22_0_0_Exfat */
};
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
@@ -692,15 +704,27 @@ namespace ams::nxboot {
AddPatch(fs_meta, 0x187B70, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_21_0_0:
case FsVersion_21_2_0:
AddPatch(fs_meta, 0x1AC9ED, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1ACA05 , NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1ACA05, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x17FBE0, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_21_0_0_Exfat:
case FsVersion_21_2_0_Exfat:
AddPatch(fs_meta, 0x1B7B4D, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1B7B65, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x18AD40, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_22_0_0:
AddPatch(fs_meta, 0x1B023D, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1B0255, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x183060, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_22_0_0_Exfat:
AddPatch(fs_meta, 0x1BB42D, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x1BB445, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x18E250, NogcPatch1, sizeof(NogcPatch1));
break;
default:
break;
}

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 6cc765fcaa5f83b9cd6dc5b424bae31a0f33b29f
parent = 3cb5d5f957020d07f5bdb3fb89521f29bea5d79d
commit = 9a8703e710760be8c88147d15414a1c581711625
parent = eb34f9789c62745d87f37e76761b14d946bac300
method = merge
cmdver = 0.4.1

View File

@@ -41,6 +41,7 @@ namespace ams::pkg1 {
KeyGeneration_19_0_0 = 0x12,
KeyGeneration_20_0_0 = 0x13,
KeyGeneration_21_0_0 = 0x14,
KeyGeneration_22_0_0 = 0x15,
KeyGeneration_Count,

View File

@@ -24,7 +24,7 @@ namespace ams::pkg2 {
constexpr inline int PayloadCount = 3;
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x18 in Nintendo's code. */
constexpr inline int CurrentBootloaderVersion = 0x18;
constexpr inline int CurrentBootloaderVersion = 0x17;
struct Package2Meta {
using Magic = util::FourCC<'P','K','2','1'>;

View File

@@ -30,6 +30,7 @@ namespace ams::secmon {
SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5),
SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6),
SecureMonitorConfigurationFlag_ForceEnableUsb30 = (1u << 7),
SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled = (1u << 8),
SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel,
};
@@ -103,6 +104,7 @@ namespace ams::secmon {
constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; }
constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags[0] & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; }
constexpr bool IsUsb30ForceEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_ForceEnableUsb30) != 0; }
constexpr bool IsBootConfigMemoryModeEnabled() const { return (this->flags[0] & SecureMonitorConfigurationFlag_BootConfigMemoryModeEnabled) != 0; }
constexpr bool IsDevelopmentFunctionEnabled(bool for_kern) const { return for_kern ? this->IsDevelopmentFunctionEnabledForKernel() : this->IsDevelopmentFunctionEnabledForUser(); }
};

View File

@@ -177,6 +177,7 @@ namespace ams::fuse {
}
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
TargetFirmware_22_0_0,
TargetFirmware_21_0_0,
TargetFirmware_20_0_0,
TargetFirmware_19_0_0,

View File

@@ -200,6 +200,7 @@ namespace ams::kern {
bool m_is_kernel;
bool m_enable_aslr;
bool m_enable_device_address_space_merge;
bool m_allowed_exec_device_mapping;
KMemoryBlockSlabManager *m_memory_block_slab_manager;
KBlockInfoManager *m_block_info_manager;
KResourceLimit *m_resource_limit;
@@ -217,7 +218,7 @@ namespace ams::kern {
m_alias_code_region_end(Null<KProcessAddress>), m_code_region_start(Null<KProcessAddress>), m_code_region_end(Null<KProcessAddress>),
m_max_heap_size(), m_mapped_physical_memory_size(), m_mapped_unsafe_physical_memory(), m_mapped_insecure_memory(), m_mapped_ipc_server_memory(), m_alias_region_extra_size(),
m_general_lock(), m_map_physical_memory_lock(), m_device_map_lock(), m_impl(util::ConstantInitialize), m_memory_block_manager(util::ConstantInitialize),
m_allocate_option(), m_address_space_width(), m_is_kernel(), m_enable_aslr(), m_enable_device_address_space_merge(),
m_allocate_option(), m_address_space_width(), m_is_kernel(), m_enable_aslr(), m_enable_device_address_space_merge(), m_allowed_exec_device_mapping(),
m_memory_block_slab_manager(), m_block_info_manager(), m_resource_limit(), m_cached_physical_linear_region(), m_cached_physical_heap_region(),
m_heap_fill_value(), m_ipc_fill_value(), m_stack_fill_value()
{
@@ -520,6 +521,8 @@ namespace ams::kern {
size_t GetAliasCodeDataSize() const;
u32 GetAllocateOption() const { return m_allocate_option; }
void AllowDeviceMappingOfExecPages() { m_allowed_exec_device_mapping = true; }
public:
static ALWAYS_INLINE KVirtualAddress GetLinearMappedVirtualAddress(KPhysicalAddress addr) {
return KMemoryLayout::GetLinearVirtualAddress(addr);

View File

@@ -102,8 +102,8 @@ namespace ams::kern {
IoRegionList m_io_region_list;
bool m_is_suspended;
bool m_is_immortal;
bool m_is_jit_debug;
bool m_is_handle_table_initialized;
bool m_is_jit_debug;
ams::svc::DebugEvent m_jit_debug_event_type;
ams::svc::DebugException m_jit_debug_exception_type;
uintptr_t m_jit_debug_params[4];

View File

@@ -285,7 +285,7 @@ namespace ams::kern {
MESOSPHERE_INIT_ABORT_UNLESS(expected_size != 0);
/* Ensure that the size we need to reserve is as we expect it to be. */
const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize);
const u32 total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize);
MESOSPHERE_ABORT_UNLESS(total_size == expected_size);
MESOSPHERE_ABORT_UNLESS(total_size <= InitialProcessBinarySizeMax);

View File

@@ -211,6 +211,7 @@ namespace ams::kern {
/* Set other basic fields. */
m_enable_aslr = (flags & ams::svc::CreateProcessFlag_EnableAslr) != 0;
m_enable_device_address_space_merge = (flags & ams::svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge) == 0;
m_allowed_exec_device_mapping = false;
m_address_space_start = start;
m_address_space_end = end;
m_is_kernel = false;
@@ -3077,7 +3078,9 @@ namespace ams::kern {
const u32 test_state = (is_aligned ? KMemoryState_FlagCanAlignedDeviceMap : KMemoryState_FlagCanDeviceMap) | (check_heap ? KMemoryState_FlagReferenceCounted : KMemoryState_None);
size_t num_allocator_blocks;
KMemoryState old_state;
R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr, std::addressof(num_allocator_blocks), address, size, test_state, test_state, perm, perm, KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None, KMemoryAttribute_DeviceShared));
const KMemoryPermission perm_mask = static_cast<KMemoryPermission>(perm | (m_allowed_exec_device_mapping ? KMemoryPermission_None : KMemoryPermission_UserExecute));
R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr, std::addressof(num_allocator_blocks), address, size, test_state, test_state, perm_mask, perm, KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None, KMemoryAttribute_DeviceShared));
/* Create an update allocator. */
Result allocator_result;

View File

@@ -185,6 +185,11 @@ namespace ams::kern {
/* Validate that the intended kernel version isn't too high for us to support. */
R_UNLESS(m_capabilities.GetIntendedKernelVersion() <= ams::svc::SupportedKernelVersion, svc::ResultInvalidCombination());
/* Enable mapping device pages as executable on legacy processes. */
if (m_capabilities.GetIntendedKernelMajorVersion() < 26) {
m_page_table.GetBasePageTable().AllowDeviceMappingOfExecPages();
}
/* Create and clear the process local region. */
R_TRY(this->CreateThreadLocalRegion(std::addressof(m_plr_address)));
m_plr_heap_address = this->GetThreadLocalRegionPointer(m_plr_address);
@@ -976,6 +981,9 @@ namespace ams::kern {
/* Set the thread arguments. */
main_thread->GetContext().SetArguments(0, thread_handle);
/* Pass the thread handle to the thread local region. */
static_cast<ams::svc::ThreadLocalRegion *>(main_thread->GetThreadLocalRegionHeapAddress())->thread_handle = thread_handle;
/* Update our state. */
this->ChangeState((state == State_Created) ? State_Running : State_RunningAttached);
ON_RESULT_FAILURE_2 { this->ChangeState(state); };

View File

@@ -66,6 +66,9 @@ namespace ams::kern::svc {
/* Add the thread to the handle table. */
R_TRY(process.GetHandleTable().Add(out, thread));
/* Pass the thread handle to the thread local region. */
static_cast<ams::svc::ThreadLocalRegion *>(thread->GetThreadLocalRegionHeapAddress())->thread_handle = *out;
R_SUCCEED();
}

View File

@@ -661,7 +661,7 @@
HANDLER(UsbControllerCount, 457, ConnectedControllerInfo, FieldType_NumericU8, FieldFlag_None ) \
HANDLER(ControllerTypeList, 458, ConnectedControllerInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(ControllerInterfaceList, 459, ConnectedControllerInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(ControllerStyleList, 460, ConnectedControllerInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(ControllerStyleListDeprecated, 460, ConnectedControllerInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(FsPooledBufferPeakFreeSize, 461, FsMemoryInfo, FieldType_NumericU64, FieldFlag_None ) \
HANDLER(FsPooledBufferRetriedCount, 462, FsMemoryInfo, FieldType_NumericU64, FieldFlag_None ) \
HANDLER(FsPooledBufferReduceAllocationCount, 463, FsMemoryInfo, FieldType_NumericU64, FieldFlag_None ) \
@@ -974,6 +974,11 @@
HANDLER(RomFsRecoveredAesFailedCount, 772, FsProxyErrorInfo3, FieldType_NumericU32, FieldFlag_None ) \
HANDLER(DriverRecoveredAesFailedCount, 773, FsProxyErrorInfo3, FieldType_NumericU32, FieldFlag_None ) \
HANDLER(BluetoothIsHalted, 774, BluetoothErrorInfo, FieldType_Bool, FieldFlag_None ) \
HANDLER(BluetoothHaltedHciCommandOpcode, 775, ErrorInfo, FieldType_NumericU16, FieldFlag_None ) \
HANDLER(AcpSupportedLanguageFlagForNxAddon, 776, AcpGeneralSettingsInfo, FieldType_NumericU32, FieldFlag_None ) \
HANDLER(FsSaveDataFileSystemPeakMountCount, 777, FsProxyErrorInfo3, FieldType_NumericI32, FieldFlag_None ) \
HANDLER(TestBool, 778, Test, FieldType_Bool, FieldFlag_None ) \
HANDLER(TestI8Array, 779, Test, FieldType_I8Array, FieldFlag_None ) \
HANDLER(TestStringNx, 1000, TestNx, FieldType_String, FieldFlag_None ) \
HANDLER(BoostModeCurrentLimit, 1001, BatteryChargeInfo, FieldType_NumericI32, FieldFlag_None ) \
HANDLER(ChargeConfiguration, 1002, BatteryChargeInfo, FieldType_NumericI32, FieldFlag_None ) \
@@ -1008,5 +1013,5 @@
HANDLER(LastDvfsThresholdTripped, 1031, ThermalInfo, FieldType_NumericI32, FieldFlag_None ) \
HANDLER(ModuleClockEnableFlags, 1032, PowerClockInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(ModulePowerEnableFlags, 1033, PowerClockInfo, FieldType_U8Array, FieldFlag_None ) \
HANDLER(BluetoothAudioConnectionCount, 1034, BluetoothAudioInfo, FieldType_NumericU8, FieldFlag_None )
HANDLER(BluetoothAudioConnectionCount, 1034, BluetoothAudioInfo, FieldType_NumericU8, FieldFlag_None ) \
HANDLER(ControllerStyleList, 1035, ConnectedControllerInfo, FieldType_U8Array, FieldFlag_None )

View File

@@ -23,7 +23,7 @@ namespace ams::erpt {
#define GENERATE_ENUM(NAME, ID, ...) NAME = ID,
enum FieldType {
enum FieldType: u8 {
AMS_ERPT_FOREACH_FIELD_TYPE(GENERATE_ENUM)
FieldType_Count,
};
@@ -111,6 +111,7 @@ namespace ams::erpt {
struct CreateReportOptionFlag {
using SubmitFsInfo = util::BitFlagSet<BITSIZEOF(u32), CreateReportOptionFlag>::Flag<0>;
using Unknown0x20000 = util::BitFlagSet<BITSIZEOF(u32), CreateReportOptionFlag>::Flag<17>; /* TODO: What is this, it's checked in Reporter::CreateReport or below */
};
using CreateReportOptionFlagSet = util::BitFlagSet<BITSIZEOF(u32), CreateReportOptionFlag>;
@@ -243,4 +244,33 @@ namespace ams::erpt {
Map16 = 0xDE,
};
constexpr inline u32 ErrorCodeSizeMax = 15;
constexpr inline u32 ProgramIdSizeMax = 17;
struct NotifiableErrorCodeReportEntry {
char error_code[ErrorCodeSizeMax];
char program_id[ProgramIdSizeMax];
u8 is_visible;
u8 is_system_abort;
u8 is_application_abort;
};
static_assert(sizeof(NotifiableErrorCodeReportEntry) == 35);
struct NotifiableErrorCodesData : public sf::LargeData, public sf::PrefersAutoSelectTransferMode {
u32 entry_count;
NotifiableErrorCodeReportEntry entries[50];
char firmware_display_version[0x18];
char private_os_version[96];
char product_model[16];
char region_code[34];
};
static_assert(sizeof(NotifiableErrorCodesData) == 0x784);
struct SystemInfo {
char os_version[0x18];
char private_os_version[96];
char product_model[16];
const char *region;
};
}

View File

@@ -25,6 +25,7 @@
AMS_SF_METHOD_INFO(C, H, 4, Result, GetStorageUsageStatistics, (ams::sf::Out<erpt::StorageUsageStatistics> out), (out), hos::Version_5_0_0) \
AMS_SF_METHOD_INFO(C, H, 5, Result, GetAttachmentListDeprecated, (const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_buf, report_id), hos::Version_8_0_0, hos::Version_19_0_1) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAttachmentList, (ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_count, out_buf, report_id), hos::Version_20_0_0) \
AMS_SF_METHOD_INFO(C, H, 7, Result, PopNotifiableErrorCodes, (ams::sf::Out<erpt::NotifiableErrorCodesData> out), (out), hos::Version_22_0_0) \
AMS_SF_METHOD_INFO(C, H, 10, Result, GetReportSizeMax, (ams::sf::Out<u32> out), (out), hos::Version_20_0_0)

View File

@@ -21,7 +21,9 @@ namespace ams::erpt::srv {
Result Initialize(u8 *mem, size_t mem_size);
Result InitializeAndStartService();
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len);
const SystemInfo &GetSystemInfo();
Result SetSerialNumber(const char *sn, u32 sn_len);
Result SetProductModel(const char *model, u32 model_len);
Result SetRegionSetting(const char *region, u32 region_len);

View File

@@ -52,7 +52,8 @@ namespace ams::fs {
struct GameCardErrorReportInfo {
u16 game_card_crc_error_num;
u16 reserved1;
u8 last_deactivate_reason;
u8 reserved1;
u16 asic_crc_error_num;
u16 reserved2;
u16 refresh_num;
@@ -73,7 +74,8 @@ namespace ams::fs {
u32 awaken_count;
u32 read_count_from_insert;
u32 read_count_from_awaken;
u8 reserved5[8];
u32 last_deactivate_reason_result;
u32 reserved5;
};
static_assert(util::is_pod<GameCardErrorReportInfo>::value);
static_assert(sizeof(GameCardErrorReportInfo) == 0x40);

View File

@@ -132,7 +132,7 @@
AMS_SF_METHOD_INFO(C, H, 1000, Result, SetBisRootForHost, (u32 id, const fssrv::sf::FspPath &path), (id, path), hos::Version_Min, hos::Version_9_2_0) \
AMS_SF_METHOD_INFO(C, H, 1001, Result, SetSaveDataSize, (s64 size, s64 journal_size), (size, journal_size)) \
AMS_SF_METHOD_INFO(C, H, 1002, Result, SetSaveDataRootPath, (const fssrv::sf::FspPath &path), (path)) \
AMS_SF_METHOD_INFO(C, H, 1003, Result, DisableAutoSaveDataCreation, (), ()) \
AMS_SF_METHOD_INFO(C, H, 1003, Result, DisableAutoSaveDataCreation, (), (), hos::Version_Min, hos::Version_21_2_0) \
AMS_SF_METHOD_INFO(C, H, 1004, Result, SetGlobalAccessLogMode, (u32 mode), (mode)) \
AMS_SF_METHOD_INFO(C, H, 1005, Result, GetGlobalAccessLogMode, (ams::sf::Out<u32> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 1006, Result, OutputAccessLogToSdCard, (const ams::sf::InBuffer &buf), (buf)) \

View File

@@ -99,6 +99,8 @@ namespace ams::hos {
Version_21_0_0 = ::ams::TargetFirmware_21_0_0,
Version_21_0_1 = ::ams::TargetFirmware_21_0_1,
Version_21_1_0 = ::ams::TargetFirmware_21_1_0,
Version_21_2_0 = ::ams::TargetFirmware_21_2_0,
Version_22_0_0 = ::ams::TargetFirmware_22_0_0,
Version_Current = ::ams::TargetFirmware_Current,

View File

@@ -94,13 +94,14 @@ namespace ams::ldr {
};
enum Flag : u32 {
Flag_CompressedText = (1 << 0),
Flag_CompressedRo = (1 << 1),
Flag_CompressedRw = (1 << 2),
Flag_CheckHashText = (1 << 3),
Flag_CheckHashRo = (1 << 4),
Flag_CheckHashRw = (1 << 5),
Flag_PreventCodeReads = (1 << 6),
Flag_CompressedText = (1 << 0),
Flag_CompressedRo = (1 << 1),
Flag_CompressedRw = (1 << 2),
Flag_CheckHashText = (1 << 3),
Flag_CheckHashRo = (1 << 4),
Flag_CheckHashRw = (1 << 5),
Flag_PreventCodeReads = (1 << 6),
Flag_UseZbicCompression = (1 << 7),
};
struct SegmentInfo {

View File

@@ -388,7 +388,7 @@
}
ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) {
R_RETURN(::svcGetDebugEvent(out_info.GetPointerUnsafe(), debug_handle));
R_RETURN(::svcGetDebugEvent(reinterpret_cast<::DebugEventInfo *>(out_info.GetPointerUnsafe()), debug_handle));
}
ALWAYS_INLINE Result ContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, ::ams::svc::UserPointer<const uint64_t *> thread_ids, int32_t num_thread_ids) {

View File

@@ -21,8 +21,11 @@ namespace ams::util {
/* Compression utilities. */
int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size);
size_t CompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size);
/* Decompression utilities. */
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size);
size_t DecompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size);
bool DecompressZstdForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size);
}

View File

@@ -19,5 +19,6 @@
namespace ams::erpt::srv {
Result SubmitFsInfo();
void ClearFsInfo();
}

View File

@@ -363,6 +363,8 @@ namespace ams::erpt::srv {
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromAwaken, ei.read_count_from_awaken));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageAddress, ei.last_read_error_page_address));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageCount, ei.last_read_error_page_count));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastDeactivateReasonResult, ei.last_deactivate_reason_result));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastDeactivateReason, ei.last_deactivate_reason));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
@@ -496,4 +498,27 @@ namespace ams::erpt::srv {
R_SUCCEED();
}
void ClearFsInfo() {
Context::ClearContext(CategoryId_NANDTypeInfo);
Context::ClearContext(CategoryId_NANDSpeedModeInfo);
Context::ClearContext(CategoryId_NANDExtendedCsd);
Context::ClearContext(CategoryId_NANDPatrolInfo);
Context::ClearContext(CategoryId_NANDErrorInfo);
Context::ClearContext(CategoryId_NANDDriverLog);
Context::ClearContext(CategoryId_MicroSDTypeInfo);
Context::ClearContext(CategoryId_MicroSDSpeedModeInfo);
Context::ClearContext(CategoryId_SdCardSizeSpec);
Context::ClearContext(CategoryId_SdCardActivationInfo);
Context::ClearContext(CategoryId_SdCardErrorInfo);
Context::ClearContext(CategoryId_SdCardDriverLog);
Context::ClearContext(CategoryId_GameCardCIDInfo);
Context::ClearContext(CategoryId_GameCardErrorInfo);
Context::ClearContext(CategoryId_GameCardDetailedErrorInfo);
Context::ClearContext(CategoryId_GameCardLogInfo);
Context::ClearContext(CategoryId_FsProxyErrorInfo);
Context::ClearContext(CategoryId_FsProxyErrorInfo2);
Context::ClearContext(CategoryId_FsProxyErrorInfo3);
Context::ClearContext(CategoryId_FsMemoryInfo);
}
}

View File

@@ -84,7 +84,7 @@ namespace ams::erpt::srv {
Result JournalForAttachments::GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id) {
if (hos::GetVersion() >= hos::Version_20_0_0) {
/* TODO: What define gives a minimum of 10? */
R_UNLESS(max_out_infos >= 10, erpt::ResultInvalidArgument());
R_UNLESS(max_out_infos >= 10, erpt::ResultTooManyOutAttachments());
}
u32 count = 0;

View File

@@ -21,6 +21,7 @@
#include "erpt_srv_journal.hpp"
#include "erpt_srv_service.hpp"
#include "erpt_srv_forced_shutdown.hpp"
#include "erpt_srv_notifiable_errors.hpp"
namespace ams::erpt::srv {
@@ -33,6 +34,7 @@ namespace ams::erpt::srv {
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData;
constexpr s64 SystemSaveDataSize = 11_MB;
constexpr s64 SystemSaveDataJournalSize = 2720_KB;
constexpr u32 DefaultThrottleTimeWindowSeconds = 3;
constinit bool g_automatic_report_cleanup_enabled = true;
@@ -53,7 +55,9 @@ namespace ams::erpt::srv {
}
Result MountSystemSaveData() {
fs::DisableAutoSaveDataCreation();
if (hos::GetVersion() < hos::Version_21_0_0) {
fs::DisableAutoSaveDataCreation();
}
/* Extend the system save data. */
/* NOTE: Nintendo used to not check the result of this; they do now, but . */
@@ -71,6 +75,72 @@ namespace ams::erpt::srv {
}
namespace {
int MakeProductModelString(char *dst, size_t dst_size, settings::system::ProductModel model) {
switch (model) {
case settings::system::ProductModel_Invalid: return util::Strlcpy(dst, "Invalid", static_cast<int>(dst_size));
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(model));
}
}
const char *GetRegionString(settings::system::RegionCode code) {
switch (code) {
case settings::system::RegionCode_Japan: return "Japan";
case settings::system::RegionCode_Usa: return "Usa";
case settings::system::RegionCode_Europe: return "Europe";
case settings::system::RegionCode_Australia: return "Australia";
case settings::system::RegionCode_HongKongTaiwanKorea: return "HongKongTaiwanKorea";
case settings::system::RegionCode_China: return "China";
default: return "RegionUnknown";
}
}
}
const erpt::SystemInfo &GetSystemInfo() {
static const erpt::SystemInfo s_info = [] {
erpt::SystemInfo info = {};
settings::system::FirmwareVersion firmware_version = {};
settings::system::GetFirmwareVersion(std::addressof(firmware_version));
util::Strlcpy(info.os_version, firmware_version.display_version, sizeof(info.os_version));
const auto os_priv_len = util::SNPrintf(info.private_os_version, sizeof(info.private_os_version), "%s (%.8s)", firmware_version.display_name, firmware_version.revision);
AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(info.private_os_version));
AMS_UNUSED(os_priv_len);
const auto pm_len = MakeProductModelString(info.product_model, sizeof(info.product_model), settings::system::GetProductModel());
AMS_ASSERT(static_cast<size_t>(pm_len) < sizeof(info.product_model));
AMS_UNUSED(pm_len);
settings::system::RegionCode region_code;
settings::system::GetRegionCode(std::addressof(region_code));
info.region = GetRegionString(region_code);
return info;
}();
return s_info;
}
u32 GetThrottleTimeWindowSecondsImpl() {
u32 seconds = DefaultThrottleTimeWindowSeconds;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(seconds), sizeof(seconds), "erpt", "throttle_time_window_seconds") != sizeof(seconds)) {
return DefaultThrottleTimeWindowSeconds;
}
return seconds;
}
void SetReportThrottleTimeSpan() {
u32 seconds = GetThrottleTimeWindowSecondsImpl();
const TimeSpan time_span = TimeSpan::FromSeconds(static_cast<s64>(seconds));
Reporter::SetThrottleTimeSpan(time_span);
}
Result Initialize(u8 *mem, size_t mem_size) {
R_ABORT_UNLESS(time::Initialize());
@@ -101,6 +171,10 @@ namespace ams::erpt::srv {
}
}
if (hos::GetVersion() >= hos::Version_22_0_0) {
SetReportThrottleTimeSpan();
}
R_ABORT_UNLESS(MountSystemSaveData());
g_sf_allocator.Attach(g_heap_handle);
@@ -110,8 +184,18 @@ namespace ams::erpt::srv {
AMS_ABORT_UNLESS(ctx != nullptr);
}
if (R_FAILED(Journal::Restore())) {
/* TODO: Nintendo deletes system savedata when this fails. Should we?. */
if (hos::GetVersion() >= hos::Version_21_0_0) {
/* >= 21.0.0, Nintendo checks the result of restore and deletes the save data if it fails. */
if (R_FAILED(Journal::Restore())) {
/* Delete and recreate the system save data. */
fs::Unmount(ReportStoragePath);
R_ABORT_UNLESS(fs::DeleteSystemSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, fs::InvalidUserId));
R_ABORT_UNLESS(MountSystemSaveData());
}
} else{
/* Pre 21.0.0, Nintendo just calls restore and ignores the result. */
Journal::Restore();
}
Reporter::UpdatePowerOnTime();
@@ -128,8 +212,8 @@ namespace ams::erpt::srv {
R_RETURN(InitializeService());
}
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) {
R_RETURN(Reporter::SetSerialNumberAndOsVersion(sn, sn_len, os, os_len, os_priv, os_priv_len));
Result SetSerialNumber(const char *sn, u32 sn_len) {
R_RETURN(Reporter::SetSerialNumber(sn, sn_len));
}
Result SetProductModel(const char *model, u32 model_len) {

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "erpt_srv_manager_impl.hpp"
#include "erpt_srv_journal.hpp"
#include "erpt_srv_notifiable_errors.hpp"
namespace ams::erpt::srv {
@@ -59,6 +60,7 @@ namespace ams::erpt::srv {
Result ManagerImpl::CleanupReports() {
Journal::CleanupReports();
Journal::CleanupAttachments();
NotifiableErrorCodeReport::Clear();
R_RETURN(Journal::Commit());
}
@@ -104,4 +106,10 @@ namespace ams::erpt::srv {
R_SUCCEED();
}
Result ManagerImpl::PopNotifiableErrorCodes(ams::sf::Out<NotifiableErrorCodesData> out) {
NotifiableErrorCodeReport::PopNotifiableErrorCodes(out.GetPointer());
R_SUCCEED();
}
}

View File

@@ -36,6 +36,7 @@ namespace ams::erpt::srv {
Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out);
Result GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
Result GetAttachmentList(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
Result PopNotifiableErrorCodes(ams::sf::Out<NotifiableErrorCodesData> out);
Result GetReportSizeMax(ams::sf::Out<u32> out);
};
static_assert(erpt::sf::IsIManager<ManagerImpl>);

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_notifiable_errors.hpp"
namespace ams::erpt::srv {
namespace {
constexpr size_t MaxEntriesPerType = 25;
struct NotifiableErrorCodeReportState {
u32 report_counts[ReportType_Count];
NotifiableErrorCodeReportEntry report_entries[ReportType_Count][MaxEntriesPerType];
os::Tick last_tick;
u32 consecutive_count;
};
constinit NotifiableErrorCodeReportState g_state = {
.report_counts = {},
.report_entries = {},
.last_tick = os::Tick{},
.consecutive_count = 0,
};
}
void NotifiableErrorCodeReport::PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort) {
u32 &count = g_state.report_counts[type];
NotifiableErrorCodeReportEntry *entries = g_state.report_entries[type];
/* If we're full, shift the oldest entry out. */
if (count >= MaxEntriesPerType) {
std::memmove(entries, entries + 1, sizeof(NotifiableErrorCodeReportEntry) * (MaxEntriesPerType - 1));
count = MaxEntriesPerType - 1;
}
/* Fill the new entry. */
NotifiableErrorCodeReportEntry &entry = entries[count];
util::Strlcpy(entry.error_code, error_code, sizeof(entry.error_code));
util::Strlcpy(entry.program_id, program_id, sizeof(entry.program_id));
entry.is_visible = (type == ReportType_Visible);
entry.is_system_abort = is_system_abort;
entry.is_application_abort = is_application_abort;
count++;
}
void NotifiableErrorCodeReport::PopNotifiableErrorCodes(NotifiableErrorCodesData *out) {
/* Fill basic info from lazily-initialized system info. */
const auto &sys_info = srv::GetSystemInfo();
util::Strlcpy(out->firmware_display_version, sys_info.os_version, sizeof(out->firmware_display_version));
util::Strlcpy(out->private_os_version, sys_info.private_os_version, sizeof(out->private_os_version));
util::Strlcpy(out->product_model, sys_info.product_model, sizeof(out->product_model));
util::Strlcpy(out->region_code, sys_info.region, sizeof(out->region_code));
u32 total_count = 0;
/* Copy entries. */
for (u32 i = 0; i < ReportType_Count; i++) {
if (g_state.report_counts[i] == 0) {
continue;
}
std::memcpy(out->entries + total_count, g_state.report_entries[i], sizeof(NotifiableErrorCodeReportEntry) * g_state.report_counts[i]);
total_count += g_state.report_counts[i];
/* Reset count (destructive read). */
g_state.report_counts[i] = 0;
}
out->entry_count = total_count;
}
void NotifiableErrorCodeReport::Clear() {
for (u32 i = 0; i < ReportType_Count; i++) {
g_state.report_counts[i] = 0;
}
}
}

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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
class NotifiableErrorCodeReport {
public:
static void PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort);
static void PopNotifiableErrorCodes(NotifiableErrorCodesData *out);
static void Clear();
};
}

View File

@@ -20,13 +20,12 @@
#include "erpt_srv_context_record.hpp"
#include "erpt_srv_context.hpp"
#include "erpt_srv_fs_info.hpp"
#include "erpt_srv_notifiable_errors.hpp"
namespace ams::erpt::srv {
constinit bool Reporter::s_redirect_new_reports = true;
constinit char Reporter::s_serial_number[24] = "Unknown";
constinit char Reporter::s_os_version[24] = "Unknown";
constinit char Reporter::s_private_os_version[96] = "Unknown";
constinit util::optional<os::Tick> Reporter::s_application_launch_time;
constinit util::optional<os::Tick> Reporter::s_awake_time;
constinit util::optional<os::Tick> Reporter::s_power_on_time;
@@ -212,18 +211,83 @@ namespace ams::erpt::srv {
}
#endif
Result ValidateCreateReportContext(const ContextEntry *ctx) {
Result ValidateAndGetErrorCode(const ContextEntry *ctx, char *out_error_code) {
R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
const bool found_error_code = util::range::any_of(MakeSpan(ctx->fields, ctx->field_count), [] (const FieldEntry &entry) {
return entry.id == FieldId_ErrorCode;
});
R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing());
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
const u8 *array_data = static_cast<const u8 *>(ctx->array_buffer);
const FieldEntry *error_code_field = nullptr;
for (const auto &field : fields_span) {
if (field.id != FieldId_ErrorCode){
continue;
}
error_code_field = &field;
break;
}
R_UNLESS(error_code_field != nullptr, erpt::ResultRequiredFieldMissing());
R_UNLESS(error_code_field->type == FieldType_String, erpt::ResultFieldTypeMismatch());
R_UNLESS(error_code_field->value_array.size <= ErrorCodeSizeMax, erpt::ResultArrayFieldTooLarge());
const char *error_code = reinterpret_cast<const char *>(array_data + error_code_field->value_array.start_idx);
util::Strlcpy(out_error_code, error_code, ErrorCodeSizeMax);
R_SUCCEED();
}
namespace {
struct ThrottleState {
TimeSpan throttle_time_span;
char last_error_code[ErrorCodeSizeMax];
u32 consecutive_count;
os::Tick last_tick;
};
constinit ThrottleState g_throttle_state = {
.throttle_time_span = TimeSpan{},
.last_error_code = {},
.consecutive_count = 0,
.last_tick = os::Tick{},
};
};
bool IsThrottledReport(const ContextEntry *ctx, ReportType type, const char *error_code) {
if (hos::GetVersion() < hos::Version_22_0_0) {
return false;
}
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
bool is_crash_report = false;
for (const auto &field : fields_span) {
if (field.id != FieldId_CrashReportFlag){
continue;
}
is_crash_report = field.value_bool;
break;
}
if(type == ReportType_Visible || is_crash_report){
return false;
}
const auto now = os::GetSystemTick();
const TimeSpan elapsed = (now - g_throttle_state.last_tick).ToTimeSpan();
if (std::strcmp(g_throttle_state.last_error_code, error_code) == 0 && elapsed < g_throttle_state.throttle_time_span) {
if (g_throttle_state.consecutive_count >= 5) {
return true;
}
g_throttle_state.consecutive_count++;
} else {
util::Strlcpy(g_throttle_state.last_error_code, error_code, sizeof(g_throttle_state.last_error_code));
g_throttle_state.last_tick = now;
g_throttle_state.consecutive_count = 1;
}
return false;
}
Result SubmitReportDefaults(const ContextEntry *ctx) {
AMS_ASSERT(ctx->category == CategoryId_ErrorInfo);
@@ -359,7 +423,11 @@ namespace ams::erpt::srv {
auto report = std::make_unique<Report>(record.get(), redirect_new_reports);
R_UNLESS(report != nullptr, erpt::ResultOutOfMemory());
auto report_guard = SCOPE_GUARD { const auto delete_res = report->Delete(); R_ASSERT(delete_res); AMS_UNUSED(delete_res); };
auto report_guard = SCOPE_GUARD {
const auto delete_res = report->Delete();
R_ASSERT(delete_res);
AMS_UNUSED(delete_res);
};
R_TRY(Context::WriteContextsToReport(report.get()));
R_TRY(report->GetSize(std::addressof(record->m_info.report_size)));
@@ -370,7 +438,7 @@ namespace ams::erpt::srv {
} else {
/* If we are redirecting new reports, we don't want to store the report in the journal. */
/* We should take this opportunity to delete any attachments associated with the report. */
R_ABORT_UNLESS(JournalForAttachments::DeleteAttachments(report_id));
R_TRY(JournalForAttachments::DeleteAttachments(report_id));
}
R_TRY(Journal::Commit());
@@ -381,6 +449,9 @@ namespace ams::erpt::srv {
}
void Reporter::SetThrottleTimeSpan(TimeSpan time_span) {
g_throttle_state.throttle_time_span = time_span;
}
Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) {
g_applet_active_time_info_list.Register(program_id);
R_SUCCEED();
@@ -422,17 +493,70 @@ namespace ams::erpt::srv {
static_cast<void>(Context::ClearContext(CategoryId_ErrorInfo));
static_cast<void>(Context::ClearContext(CategoryId_ErrorInfoAuto));
static_cast<void>(Context::ClearContext(CategoryId_ErrorInfoDefaults));
#if defined(ATMOSPHERE_OS_HORIZON)
/* TODO: What else is missing? */
if (hos::GetVersion() >= hos::Version_17_0_0 && flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
ClearFsInfo();
}
/* if (erpt::ResultInvalidPowerState::Includes(...)) {
* Nintendo ignores this and sends "power_state_violation" play report if this error happens.
* } else {
* Nintendo sends "write_failure" play report if any other error happens.
* }
*/
#endif
};
/* Get the context entry pointer. */
const ContextEntry *ctx = record->GetContextEntryPtr();
/* Validate the context. */
R_TRY(ValidateCreateReportContext(ctx));
/* Validate the context and retrieve the error code. */
char error_code[ErrorCodeSizeMax];
R_TRY(ValidateAndGetErrorCode(ctx, error_code));
if (hos::GetVersion() >= hos::Version_22_0_0) {
/* Check if we should throttle the report. */
if (IsThrottledReport(ctx, type, error_code)) {
R_SUCCEED();
}
}
/* Submit report defaults. */
R_TRY(SubmitReportDefaults(ctx));
/* Push to recent reports. */
if (hos::GetVersion() >= hos::Version_22_0_0) {
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
const u8 *array_data = static_cast<const u8 *>(ctx->array_buffer);
char program_id[ProgramIdSizeMax] = {};
bool is_system_abort = false;
bool is_application_abort = false;
for (const auto &field : fields_span) {
switch (field.id) {
case FieldId_ProgramId:
if(field.type != FieldType_String){
break;
}
util::Strlcpy(program_id, reinterpret_cast<const char *>(array_data + field.value_array.start_idx), sizeof(program_id));
break;
case FieldId_SystemAbortFlag:
is_system_abort = field.value_bool;
break;
case FieldId_ApplicationAbortFlag:
is_application_abort = field.value_bool;
break;
default:
break;
}
}
NotifiableErrorCodeReport::PushEntry(error_code, program_id, type, is_system_abort, is_application_abort);
}
/* Generate report id. */
const ReportId report_id = specified_report_id ? *specified_report_id : ReportId{ .uuid = util::GenerateUuid() };
@@ -480,8 +604,9 @@ namespace ams::erpt::srv {
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint)));
/* Add automatic fields. */
static_cast<void>(auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version))));
static_cast<void>(auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version))));
const auto &sys_info = srv::GetSystemInfo();
static_cast<void>(auto_record->Add(FieldId_OsVersion, sys_info.os_version, util::Strnlen(sys_info.os_version, sizeof(sys_info.os_version))));
static_cast<void>(auto_record->Add(FieldId_PrivateOsVersion, sys_info.private_os_version, util::Strnlen(sys_info.private_os_version, sizeof(sys_info.private_os_version))));
static_cast<void>(auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))));
static_cast<void>(auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str))));
static_cast<void>(auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value));

View File

@@ -23,8 +23,6 @@ namespace ams::erpt::srv {
private:
static bool s_redirect_new_reports;
static char s_serial_number[24];
static char s_os_version[24];
static char s_private_os_version[96];
static util::optional<os::Tick> s_application_launch_time;
static util::optional<os::Tick> s_awake_time;
static util::optional<os::Tick> s_power_on_time;
@@ -39,14 +37,11 @@ namespace ams::erpt::srv {
static void UpdateAwakeTime() { s_awake_time = os::GetSystemTick(); }
static void UpdatePowerOnTime() { s_power_on_time = os::GetSystemTick(); }
static Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) {
R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument());
R_UNLESS(os_len <= sizeof(s_os_version), erpt::ResultInvalidArgument());
R_UNLESS(os_priv_len <= sizeof(s_private_os_version), erpt::ResultInvalidArgument());
static void SetThrottleTimeSpan(TimeSpan time_span);
static Result SetSerialNumber(const char *sn, u32 sn_len) {
R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument());
std::memcpy(s_serial_number, sn, sn_len);
std::memcpy(s_os_version, os, os_len);
std::memcpy(s_private_os_version, os_priv, os_priv_len);
R_SUCCEED();
}

View File

@@ -42,6 +42,10 @@ namespace ams::fs {
}
void DisableAutoSaveDataCreation() {
if (hos::GetVersion() >= hos::Version_22_0_0) {
return;
}
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_ABORT_UNLESS(fsp->DisableAutoSaveDataCreation());
}

View File

@@ -21,7 +21,7 @@ namespace ams::fs::impl {
#define ADD_ENUM_CASE(v) case v: return #v
template<> const char *IdString::ToString<pkg1::KeyGeneration>(pkg1::KeyGeneration id) {
static_assert(pkg1::KeyGeneration_Current == pkg1::KeyGeneration_21_0_0);
static_assert(pkg1::KeyGeneration_Current == pkg1::KeyGeneration_22_0_0);
switch (id) {
using enum pkg1::KeyGeneration;
case KeyGeneration_1_0_0: return "1.0.0-2.3.0";
@@ -44,7 +44,8 @@ namespace ams::fs::impl {
case KeyGeneration_18_0_0: return "18.0.0-18.1.0";
case KeyGeneration_19_0_0: return "19.0.0-19.0.1";
case KeyGeneration_20_0_0: return "20.0.0-20.5.0";
case KeyGeneration_21_0_0: return "21.0.0-";
case KeyGeneration_21_0_0: return "21.0.0-21.2.0";
case KeyGeneration_22_0_0: return "22.0.0";
default: return "Unknown";
}
}

View File

@@ -75,15 +75,15 @@ namespace ams::pgl::srv {
NON_COPYABLE(HostPackageReader);
NON_MOVEABLE(HostPackageReader);
private:
char m_content_path[fs::EntryNameLengthMax] = {};
ExtensionType m_extension_type = ExtensionType::None;
char m_mount_name[fs::MountNameLengthMax] = {};
bool m_is_mounted = false;
char m_content_path[fs::EntryNameLengthMax] = {};
ExtensionType m_extension_type = ExtensionType::None;
char m_mount_name[fs::MountNameLengthMax + 1] = {};
bool m_is_mounted = false;
ncm::AutoBuffer m_content_meta_buffer;
ncm::ProgramId m_program_id = ncm::InvalidProgramId;
u32 m_program_version = 0;
ncm::ContentMetaType m_content_meta_type = static_cast<ncm::ContentMetaType>(0);
u8 m_program_index = 0;
ncm::ProgramId m_program_id = ncm::InvalidProgramId;
u32 m_program_version = 0;
ncm::ContentMetaType m_content_meta_type = static_cast<ncm::ContentMetaType>(0);
u8 m_program_index = 0;
public:
HostPackageReader() : m_content_meta_buffer() { /* ... */ }
~HostPackageReader() {

View File

@@ -17,6 +17,7 @@
#include "pinmux_pad_index.hpp"
#include "pinmux_board_driver_api.hpp"
#include "pinmux_platform_pads.hpp"
#include "pinmux_build_config.hpp"
namespace ams::pinmux::driver::board::nintendo::nx {
@@ -99,6 +100,31 @@ namespace ams::pinmux::driver::board::nintendo::nx {
UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat6, 0x2000, 0x2000 });
UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat7, 0x2000, 0x2000 });
}
#if defined(AMS_PINMUX_CONFIG_RIGHT_RAIL_AS_UART)
UpdateSinglePinmuxPad({
.index = PinmuxPadIndex_Uart2Tx,
.option = 0, /* PinmuxPadPm_Pm0 | PinmuxOpt_NoPupd | PinmuxOpt_Output */
.option_mask = (0x7|0x18|0x60), /* PinmuxOptBitMask_Pm | PinmuxOptBitMask_Pupd | PinmuxOptBitMask_Dir */
});
UpdateSinglePinmuxPad({
.index = PinmuxPadIndex_Uart2Cts,
.option = 0x20, /* PinmuxPadPm_Pm0 | PinmuxOpt_NoPupd | PinmuxOpt_Input */
.option_mask = (0x7|0x18|0x60), /* PinmuxOptBitMask_Pm | PinmuxOptBitMask_Pupd | PinmuxOptBitMask_Dir */
});
#endif
#if defined(AMS_PINMUX_CONFIG_LEFT_RAIL_AS_UART)
UpdateSinglePinmuxPad({
.index = PinmuxPadIndex_Uart3Tx,
.option = 0, /* PinmuxPadPm_Pm0 | PinmuxOpt_NoPupd | PinmuxOpt_Output */
.option_mask = (0x7|0x18|0x60), /* PinmuxOptBitMask_Pm | PinmuxOptBitMask_Pupd | PinmuxOptBitMask_Dir */
});
UpdateSinglePinmuxPad({
.index = PinmuxPadIndex_Uart3Cts,
.option = 0x20, /* PinmuxPadPm_Pm0 | PinmuxOpt_NoPupd | PinmuxOpt_Input */
.option_mask = (0x7|0x18|0x60), /* PinmuxOptBitMask_Pm | PinmuxOptBitMask_Pupd | PinmuxOptBitMask_Dir */
});
#endif
}
void SetInitialDrivePadConfig() {

View File

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

View File

@@ -15,6 +15,9 @@
*/
#include <stratosphere.hpp>
#include "lz4.h"
#define ZSTD_STATIC_LINKING_ONLY
#define ZSTD_ZBIC_SUPPORT 1
#include "zstd.h"
namespace ams::util {
@@ -27,6 +30,23 @@ namespace ams::util {
/* This is just a thin wrapper around LZ4. */
return LZ4_compress_default(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
}
size_t CompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size) {
/* Basic size checks. */
AMS_ABORT_UNLESS(dst_size <= std::numeric_limits<int>::max());
AMS_ABORT_UNLESS(src_size <= std::numeric_limits<int>::max());
/* Additionally, we must check the compression boundary. */
auto bound = ZSTD_compressBound(src_size);
AMS_ABORT_UNLESS(!ZSTD_isError(bound));
AMS_ABORT_UNLESS(dst_size >= bound);
/* Use Zstd default level. */
int compressionLevel = 3;
/* This is just a wrapper around Zstd. */
return ZSTD_compress(dst, dst_size, src, src_size, compressionLevel);
}
/* Decompression utilities. */
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
@@ -37,5 +57,54 @@ namespace ams::util {
/* This is just a thin wrapper around LZ4. */
return LZ4_decompress_safe(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
}
size_t DecompressZstd(void *dst, size_t dst_size, const void *src, size_t src_size) {
/* Basic size checks. */
AMS_ABORT_UNLESS(dst_size <= std::numeric_limits<int>::max());
AMS_ABORT_UNLESS(src_size <= std::numeric_limits<int>::max());
/* Additionally, we must check the decompression boundary. */
auto bound = ZSTD_decompressBound(src, src_size);
AMS_ABORT_UNLESS(!ZSTD_isError(bound));
AMS_ABORT_UNLESS(dst_size >= bound);
/* This is just a wrapper around Zstd. */
return ZSTD_decompress(dst, dst_size, src, src_size);
}
bool DecompressZstdForLoader(void* workspace, size_t workspace_size, void *dst, size_t dst_size, size_t expected_dec_size, const void *src, size_t src_size) {
/* Check decompression margin. */
auto margin = ZSTD_decompressionMargin(src, src_size);
if (ZSTD_isError(margin)) {
return false;
}
/* Don't overflow from margin. */
if (!util::CanAddWithoutOverflow(margin, expected_dec_size)) {
return false;
}
/* Make sure we fit in the destination buffer. */
if (margin + expected_dec_size > dst_size) {
return false;
}
/* This is a runtime assert in Loader code. We replicate it here. */
AMS_ABORT_UNLESS(ZSTD_estimateDCtxSize() == workspace_size);
/* Decompress using a static decompression context. */
auto dctx = ZSTD_initStaticDCtx(workspace, workspace_size);
size_t dec_size = ZSTD_decompressDCtx(dctx, dst, dst_size, src, src_size);
if (ZSTD_isError(dec_size)) {
return false;
}
if (dec_size != expected_dec_size) {
return false;
}
return true;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/
#ifndef ZSTD_ERRORS_H_398273423
#define ZSTD_ERRORS_H_398273423
#if defined (__cplusplus)
extern "C" {
#endif
/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */
#ifndef ZSTDERRORLIB_VISIBLE
/* Backwards compatibility with old macro name */
# ifdef ZSTDERRORLIB_VISIBILITY
# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY
# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default")))
# else
# define ZSTDERRORLIB_VISIBLE
# endif
#endif
#ifndef ZSTDERRORLIB_HIDDEN
# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden")))
# else
# define ZSTDERRORLIB_HIDDEN
# endif
#endif
#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE
#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE
#endif
/*-*********************************************
* Error codes list
*-*********************************************
* Error codes _values_ are pinned down since v1.3.1 only.
* Therefore, don't rely on values if you may link to any version < v1.3.1.
*
* Only values < 100 are considered stable.
*
* note 1 : this API shall be used with static linking only.
* dynamic linking is not yet officially supported.
* note 2 : Prefer relying on the enum than on its value whenever possible
* This is the only supported way to use the error list < v1.3.1
* note 3 : ZSTD_isError() is always correct, whatever the library version.
**********************************************/
typedef enum {
ZSTD_error_no_error = 0,
ZSTD_error_GENERIC = 1,
ZSTD_error_prefix_unknown = 10,
ZSTD_error_version_unsupported = 12,
ZSTD_error_frameParameter_unsupported = 14,
ZSTD_error_frameParameter_windowTooLarge = 16,
ZSTD_error_corruption_detected = 20,
ZSTD_error_checksum_wrong = 22,
ZSTD_error_literals_headerWrong = 24,
ZSTD_error_dictionary_corrupted = 30,
ZSTD_error_dictionary_wrong = 32,
ZSTD_error_dictionaryCreation_failed = 34,
ZSTD_error_parameter_unsupported = 40,
ZSTD_error_parameter_combination_unsupported = 41,
ZSTD_error_parameter_outOfBound = 42,
ZSTD_error_tableLog_tooLarge = 44,
ZSTD_error_maxSymbolValue_tooLarge = 46,
ZSTD_error_maxSymbolValue_tooSmall = 48,
ZSTD_error_cannotProduce_uncompressedBlock = 49,
ZSTD_error_stabilityCondition_notRespected = 50,
ZSTD_error_stage_wrong = 60,
ZSTD_error_init_missing = 62,
ZSTD_error_memory_allocation = 64,
ZSTD_error_workSpace_tooSmall= 66,
ZSTD_error_dstSize_tooSmall = 70,
ZSTD_error_srcSize_wrong = 72,
ZSTD_error_dstBuffer_null = 74,
ZSTD_error_noForwardProgress_destFull = 80,
ZSTD_error_noForwardProgress_inputEmpty = 82,
/* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
ZSTD_error_frameIndex_tooLarge = 100,
ZSTD_error_seekableIO = 102,
ZSTD_error_dstBuffer_wrong = 104,
ZSTD_error_srcBuffer_wrong = 105,
ZSTD_error_sequenceProducer_failed = 106,
ZSTD_error_externalSequences_invalid = 107,
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
} ZSTD_ErrorCode;
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
#if defined (__cplusplus)
}
#endif
#endif /* ZSTD_ERRORS_H_398273423 */

View File

@@ -16,11 +16,11 @@
#pragma once
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 10
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 11
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 21
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 22
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0

View File

@@ -97,8 +97,10 @@
#define ATMOSPHERE_TARGET_FIRMWARE_21_0_0 ATMOSPHERE_TARGET_FIRMWARE(21, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_21_0_1 ATMOSPHERE_TARGET_FIRMWARE(21, 0, 1)
#define ATMOSPHERE_TARGET_FIRMWARE_21_1_0 ATMOSPHERE_TARGET_FIRMWARE(21, 1, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_21_2_0 ATMOSPHERE_TARGET_FIRMWARE(21, 2, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_22_0_0 ATMOSPHERE_TARGET_FIRMWARE(22, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_21_1_0
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_22_0_0
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
@@ -188,6 +190,8 @@ namespace ams {
TargetFirmware_21_0_0 = ATMOSPHERE_TARGET_FIRMWARE_21_0_0,
TargetFirmware_21_0_1 = ATMOSPHERE_TARGET_FIRMWARE_21_0_1,
TargetFirmware_21_1_0 = ATMOSPHERE_TARGET_FIRMWARE_21_1_0,
TargetFirmware_21_2_0 = ATMOSPHERE_TARGET_FIRMWARE_21_2_0,
TargetFirmware_22_0_0 = ATMOSPHERE_TARGET_FIRMWARE_22_0_0,
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,

View File

@@ -40,5 +40,6 @@ namespace ams::erpt {
R_DEFINE_ERROR_RESULT(InvalidPowerState, 17);
R_DEFINE_ERROR_RESULT(ArrayFieldTooLarge, 18);
R_DEFINE_ERROR_RESULT(AlreadyOwned, 19);
R_DEFINE_ERROR_RESULT(TooManyOutAttachments, 51);
}

View File

@@ -452,6 +452,9 @@ namespace ams::result::impl {
} \
}
/// Explicitly discards the result returned by an expression.
#define R_DISCARD(res_expr) (static_cast<void>(res_expr))
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_IS_STRATOSPHERE) && !defined(AMS_ENABLE_DETAILED_ASSERTIONS) && !defined(AMS_BUILD_FOR_DEBUGGING) && !defined(AMS_BUILD_FOR_AUDITING)
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)

View File

@@ -27,8 +27,9 @@ namespace ams::svc::arch::arm {
volatile u16 interrupt_flag;
volatile u8 cache_maintenance_flag;
volatile s64 thread_cpu_time;
volatile ams::svc::Handle thread_handle;
/* TODO: Should we bother adding the Nintendo aarch32 thread local context here? */
uintptr_t TODO[(0x200 - 0x110) / sizeof(uintptr_t)];
uintptr_t TODO[(0x200 - 0x114) / sizeof(uintptr_t)];
};
ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() {

View File

@@ -27,8 +27,9 @@ namespace ams::svc::arch::arm64 {
volatile u16 interrupt_flag;
volatile u8 cache_maintenance_flag;
volatile s64 thread_cpu_time;
volatile ams::svc::Handle thread_handle;
/* TODO: How should we handle libnx vs Nintendo user thread local space? */
uintptr_t TODO[(0x200 - 0x110) / sizeof(uintptr_t)];
uintptr_t TODO[(0x200 - 0x114) / sizeof(uintptr_t)];
};
static_assert(__builtin_offsetof(ThreadLocalRegion, disable_count) == 0x100);
static_assert(__builtin_offsetof(ThreadLocalRegion, interrupt_flag) == 0x102);

View File

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

View File

@@ -26,26 +26,6 @@ namespace ams {
}
int MakeProductModelString(char *dst, size_t dst_size, settings::system::ProductModel model) {
switch (model) {
case settings::system::ProductModel_Invalid: return util::Strlcpy(dst, "Invalid", static_cast<int>(dst_size));
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(model));
}
}
const char *GetRegionString(settings::system::RegionCode code) {
switch (code) {
case settings::system::RegionCode_Japan: return "Japan";
case settings::system::RegionCode_Usa: return "Usa";
case settings::system::RegionCode_Europe: return "Europe";
case settings::system::RegionCode_Australia: return "Australia";
case settings::system::RegionCode_HongKongTaiwanKorea: return "HongKongTaiwanKorea";
case settings::system::RegionCode_China: return "China";
default: return "RegionUnknown";
}
}
}
namespace init {
@@ -94,42 +74,19 @@ namespace ams {
/* Atmosphere always wants to redirect new reports to the SD card, to prevent them from being logged. */
erpt::srv::SetRedirectNewReportsToSdCard(true);
/* Configure the OS version. */
/* Configure the serial number, OS version, product model, and region. */
{
settings::system::FirmwareVersion firmware_version = {};
const auto &sys_info = erpt::srv::GetSystemInfo();
settings::system::SerialNumber serial_number = {};
settings::system::GetFirmwareVersion(std::addressof(firmware_version));
settings::system::GetSerialNumber(std::addressof(serial_number));
char os_private[0x60];
const auto os_priv_len = util::SNPrintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision);
AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(os_private));
AMS_UNUSED(os_priv_len);
R_ABORT_UNLESS(erpt::srv::SetSerialNumber(serial_number.str,
strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1));
R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str,
strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1,
firmware_version.display_version,
strnlen(firmware_version.display_version, sizeof(firmware_version.display_version) - 1) + 1,
os_private,
strnlen(os_private, sizeof(os_private) - 1) + 1));
}
R_ABORT_UNLESS(erpt::srv::SetProductModel(sys_info.product_model, static_cast<u32>(std::strlen(sys_info.product_model))));
/* Configure the product model. */
{
char product_model[0x10];
const auto pm_len = erpt::MakeProductModelString(product_model, sizeof(product_model), settings::system::GetProductModel());
AMS_ASSERT(static_cast<size_t>(pm_len) < sizeof(product_model));
AMS_UNUSED(pm_len);
R_ABORT_UNLESS(erpt::srv::SetProductModel(product_model, static_cast<u32>(std::strlen(product_model))));
}
/* Configure the region. */
{
settings::system::RegionCode code;
settings::system::GetRegionCode(std::addressof(code));
const char *region_str = erpt::GetRegionString(code);
R_ABORT_UNLESS(erpt::srv::SetRegionSetting(region_str, static_cast<u32>(std::strlen(region_str))));
R_ABORT_UNLESS(erpt::srv::SetRegionSetting(sys_info.region, static_cast<u32>(std::strlen(sys_info.region))));
}
/* Start the erpt server. */

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Patch teardown function call to NOP. */
constexpr inline const EmbeddedPatchEntry AmDisableTeardownPatches_22_0_0[] = {
{ 0x5CC4C, "\x1F\x20\x03\xD5", 4 },
};
constexpr inline const EmbeddedPatch AmDisableTeardownPatches[] = {
{ ParseModuleId("B337F7C5AD53E55F3D920FCD394B4DE7C3B7D606"), util::size(AmDisableTeardownPatches_22_0_0), AmDisableTeardownPatches_22_0_0 }, /* 22.0.0 */
};

View File

@@ -74,16 +74,16 @@ constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_20_0_0[] = {
{ 0x7090, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_20_1_0[] = {
{ 0x7010, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x7090, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_21_0_0[] = {
{ 0x6D60, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x6DE0, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatchEntry Usb30ForceEnablePatches_22_0_0[] = {
{ 0x6C90, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
{ 0x6D10, "\x20\x00\x80\x52\xC0\x03\x5F\xD6", 8 },
};
constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = {
{ ParseModuleId("C0D3F4E87E8B0FE9BBE9F1968A20767F3DC08E03"), util::size(Usb30ForceEnablePatches_9_0_0), Usb30ForceEnablePatches_9_0_0 },
{ ParseModuleId("B9C700CA8335F8BAA0D2041D8D09F772890BA988"), util::size(Usb30ForceEnablePatches_10_0_0), Usb30ForceEnablePatches_10_0_0 },
@@ -98,6 +98,7 @@ constexpr inline const EmbeddedPatch Usb30ForceEnablePatches[] = {
{ ParseModuleId("4F21AE15E814FA46515C0401BB23D4F7ADCBF3F4"), util::size(Usb30ForceEnablePatches_18_0_0), Usb30ForceEnablePatches_18_0_0 }, /* 18.0.0 */
{ ParseModuleId("54BB9BB32C958E02752DC5E4AE8D016BFE1F5418"), util::size(Usb30ForceEnablePatches_19_0_0), Usb30ForceEnablePatches_19_0_0 }, /* 19.0.0 */
{ ParseModuleId("40E80E7442C0DFC985315E6F9E8C77229818AC0F"), util::size(Usb30ForceEnablePatches_20_0_0), Usb30ForceEnablePatches_20_0_0 }, /* 20.0.0 */
{ ParseModuleId("A5EF8D22EDF8A384E4135270ED596C1D2D524159"), util::size(Usb30ForceEnablePatches_20_1_0), Usb30ForceEnablePatches_20_1_0 }, /* 20.1.0 - 20.5.0 */
{ ParseModuleId("A5EF8D22EDF8A384E4135270ED596C1D2D524159"), util::size(Usb30ForceEnablePatches_20_0_0), Usb30ForceEnablePatches_20_0_0 }, /* 20.1.0 - 20.5.0 */
{ ParseModuleId("766D0C2277207B40AD9B8DE309396D50CCE94885"), util::size(Usb30ForceEnablePatches_21_0_0), Usb30ForceEnablePatches_21_0_0 }, /* 21.0.0 */
{ ParseModuleId("B734339F2280170AF0200573F9B943242CEF8C15"), util::size(Usb30ForceEnablePatches_22_0_0), Usb30ForceEnablePatches_22_0_0 }, /* 22.0.0 */
};

View File

@@ -108,6 +108,7 @@ namespace ams::ldr {
};
#include "ldr_embedded_usb_patches.inc"
#include "ldr_embedded_am_patches.inc"
}
@@ -140,6 +141,18 @@ namespace ams::ldr {
}
}
}
/* TODO: Remove this if/when a cleaner solution is implemented by hbmenu/libnx. */
for (const auto &patch : AmDisableTeardownPatches) {
if (std::memcmp(std::addressof(patch.module_id), std::addressof(module_id), sizeof(module_id)) == 0) {
for (size_t i = 0; i < patch.num_entries; ++i) {
const auto &entry = patch.entries[i];
if (entry.offset + entry.size <= mapped_size) {
std::memcpy(reinterpret_cast<void *>(mapped_nso + entry.offset), entry.data, entry.size);
}
}
}
}
}
}

View File

@@ -92,6 +92,8 @@ namespace ams::ldr {
struct ProcessInfo {
os::NativeHandle process_handle;
uintptr_t code_address;
size_t total_size;
uintptr_t args_address;
size_t args_size;
uintptr_t nso_address[Nso_Count];
@@ -103,12 +105,25 @@ namespace ams::ldr {
bool has_main;
bool has_sdk;
bool has_subsdk;
bool has_nso[Nso_Count];
s8 nso_indices[Nso_Count];
};
struct AutoLoadModuleContext {
NsoHeader *headers;
int nso_count;
int rtld_idx;
int main_nso_idx;
int sdk_nso_idx;
AutoLoadModuleInfo ali;
};
/* Global NSO header cache. */
NsoHeader g_nso_headers[Nso_Count];
/* Global Zstd decompression context. */
constexpr size_t ZstdDctxWorkspaceSize = 0x176E8;
alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize];
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
/* No version verification is done before 8.1.0. */
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
@@ -169,10 +184,15 @@ namespace ams::ldr {
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
}
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, AutoLoadModuleInfo *ali, u32 acid_flags) {
Result LoadAutoLoadHeaders(AutoLoadModuleContext &ctx, u32 acid_flags) {
/* Clear NSOs. */
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
*ali = {};
std::memset(g_nso_headers, 0, sizeof(g_nso_headers));
ctx.headers = g_nso_headers;
ctx.nso_count = 0;
ctx.rtld_idx = -1;
ctx.main_nso_idx = -1;
ctx.sdk_nso_idx = -1;
ctx.ali = {};
for (size_t i = 0; i < Nso_Count; i++) {
/* Only load browser DLLs if acid flags say to do so. */
@@ -199,16 +219,18 @@ namespace ams::ldr {
/* Read NSO header. */
size_t read_size;
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
R_UNLESS(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso());
/* Note nso is present. */
switch (i) {
case Nso_Rtld:
ali->has_rtld = true;
ctx.rtld_idx = ctx.nso_count;
ctx.ali.has_rtld = true;
break;
case Nso_Main:
ali->has_main = true;
ctx.main_nso_idx = ctx.nso_count;
ctx.ali.has_main = true;
break;
case Nso_SubSdk0:
case Nso_SubSdk1:
@@ -220,64 +242,65 @@ namespace ams::ldr {
case Nso_SubSdk7:
case Nso_SubSdk8:
case Nso_SubSdk9:
ali->has_subsdk = true;
ctx.ali.has_subsdk = true;
break;
case Nso_Sdk:
ali->has_sdk = true;
ctx.sdk_nso_idx = ctx.nso_count;
ctx.ali.has_sdk = true;
break;
}
ali->has_nso[i] = true;
ctx.ali.nso_indices[ctx.nso_count] = static_cast<s8>(i);
ctx.nso_count++;
}
}
R_SUCCEED();
}
Result CheckAutoLoad(const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, u32 acid_flags) {
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
/* We must always have a main. */
R_UNLESS(ali->has_main, ldr::ResultInvalidNso());
R_UNLESS(ctx.ali.has_main, ldr::ResultInvalidNso());
/* All NSOs must not be --X. */
/* This is "probably" not checked on Ounce? */
for (size_t i = 0; i < Nso_Count; ++i) {
R_UNLESS((nso_headers[i].flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
}
/* Validate flags and extents for all present NSOs. */
for (int i = 0; i < ctx.nso_count; ++i) {
const auto &hdr = ctx.headers[i];
/* If we don't have an RTLD, we must only have a main. */
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
if (!ali->has_rtld) {
/* If don't have rtld, we must also not have sdk. */
R_UNLESS(!ali->has_sdk, ldr::ResultInvalidNso());
/* All NSOs must not be --X. */
/* This is "probably" not checked on Ounce? */
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
/* We must also not have both subsdk and browser dll. */
R_UNLESS(!(ali->has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
} else {
/* If we have rtld, we must not have browser core dll. */
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
}
/* Check NSO extents. */
for (size_t i = 0; i < Nso_Count; i++) {
/* Only validate the nsos we have. */
if (!ali->has_nso[i]) {
continue;
/* Zstd compression only allowed on main, and only when both rtld+sdk are present. */
if (i != ctx.main_nso_idx || ctx.rtld_idx < 0 || ctx.sdk_nso_idx < 0) {
R_UNLESS((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso());
}
/* NSOs must have page-aligned segments. */
R_UNLESS(util::IsAligned(nso_headers[i].text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
R_UNLESS(util::IsAligned(nso_headers[i].ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
R_UNLESS(util::IsAligned(nso_headers[i].rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
R_UNLESS(util::IsAligned(hdr.text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
R_UNLESS(util::IsAligned(hdr.ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
R_UNLESS(util::IsAligned(hdr.rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
/* NSOs must have zero text offset. */
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
R_UNLESS(hdr.text_dst_offset == 0, ldr::ResultInvalidNso());
/* NSO .text must precede .rodata. */
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
R_UNLESS(text_end <= static_cast<size_t>(nso_headers[i].ro_dst_offset), ldr::ResultInvalidNso());
const size_t text_end = static_cast<size_t>(hdr.text_dst_offset) + static_cast<size_t>(hdr.text_size);
R_UNLESS(text_end <= static_cast<size_t>(hdr.ro_dst_offset), ldr::ResultInvalidNso());
/* NSO .rodata must precede .rwdata. */
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
R_UNLESS(ro_end <= static_cast<size_t>(nso_headers[i].rw_dst_offset), ldr::ResultInvalidNso());
const size_t ro_end = static_cast<size_t>(hdr.ro_dst_offset) + static_cast<size_t>(hdr.ro_size);
R_UNLESS(ro_end <= static_cast<size_t>(hdr.rw_dst_offset), ldr::ResultInvalidNso());
}
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
if (ctx.ali.has_rtld || ctx.ali.has_sdk) {
/* If we have sdk we must have rtld. */
R_UNLESS(ctx.ali.has_rtld, ldr::ResultInvalidNso());
/* If we have rtld, we must not have browser core dll. */
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
} else {
/* We must not have both subsdk and browser dll. */
R_UNLESS(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
}
R_SUCCEED();
@@ -293,8 +316,8 @@ namespace ams::ldr {
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
{ 0x01006FB00F50E000 }, /* [???] */
{ 0x010070300F50C000 }, /* [???] */
{ 0x01006FB00F50E000 }, /* 宝可梦 走吧!伊布 [Pokemon: Let's Go, Eevee! for China] */
{ 0x010070300F50C000 }, /* 宝可梦 走吧!皮卡丘 [Pokemon: Let's Go, Pikachu! for China] */
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
@@ -514,7 +537,7 @@ namespace ams::ldr {
return rand % (max + 1);
}
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
/* Clear output. */
out->args_address = 0;
out->args_size = 0;
@@ -525,35 +548,33 @@ namespace ams::ldr {
bool argument_allocated = false;
/* Calculate base offsets. */
for (size_t i = 0; i < Nso_Count; i++) {
if (ali->has_nso[i]) {
out->nso_address[i] = total_size;
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
const size_t rw_end = static_cast<size_t>(nso_headers[i].rw_dst_offset) + static_cast<size_t>(nso_headers[i].rw_size);
out->nso_size[i] = text_end;
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
out->nso_size[i] += static_cast<size_t>(nso_headers[i].bss_size);
for (int i = 0; i < ctx.nso_count; i++) {
out->nso_address[i] = total_size;
const size_t text_end = static_cast<size_t>(ctx.headers[i].text_dst_offset) + static_cast<size_t>(ctx.headers[i].text_size);
const size_t ro_end = static_cast<size_t>(ctx.headers[i].ro_dst_offset) + static_cast<size_t>(ctx.headers[i].ro_size);
const size_t rw_end = static_cast<size_t>(ctx.headers[i].rw_dst_offset) + static_cast<size_t>(ctx.headers[i].rw_size);
out->nso_size[i] = text_end;
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
out->nso_size[i] += static_cast<size_t>(ctx.headers[i].bss_size);
const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1);
R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso());
R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso());
const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1);
R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso());
R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso());
out->nso_size[i] = aligned_up_size;
out->nso_size[i] = aligned_up_size;
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
total_size += out->nso_size[i];
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
total_size += out->nso_size[i];
if (!argument_allocated && argument != nullptr) {
out->args_address = total_size;
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
if (!argument_allocated && argument != nullptr) {
out->args_address = total_size;
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
total_size += out->args_size;
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
total_size += out->args_size;
argument_allocated = true;
}
argument_allocated = true;
}
}
@@ -598,11 +619,9 @@ namespace ams::ldr {
/* Set out. */
aslr_start += aslr_slide;
for (size_t i = 0; i < Nso_Count; i++) {
if (ali->has_nso[i]) {
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
out->nso_address[i] += aslr_start;
}
for (int i = 0; i < ctx.nso_count; i++) {
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
out->nso_address[i] += aslr_start;
}
if (out->args_address) {
R_UNLESS(util::CanAddWithoutOverflow(out->args_address, aslr_start), ldr::ResultInvalidNso());
@@ -611,69 +630,88 @@ namespace ams::ldr {
out_param->code_address = aslr_start;
out_param->code_num_pages = total_size >> 12;
out->total_size = total_size;
R_SUCCEED();
}
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
Result LoadAutoLoadModuleSegment(fs::FileHandle file, size_t file_offset, size_t compressed_size, size_t segment_size, bool is_compressed, bool is_zstd, uintptr_t map_base, uintptr_t map_end) {
/* Select read size based on compression. */
if (!is_compressed) {
file_size = segment->size;
}
size_t file_size = is_compressed ? compressed_size : segment_size;
/* Validate size. */
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
R_UNLESS(file_size <= segment_size, ldr::ResultInvalidNso());
R_UNLESS(file_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
R_UNLESS(segment_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
/* Load data from file. */
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
uintptr_t load_address = is_compressed ? map_end - compressed_size : map_base;
size_t read_size;
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
R_TRY(fs::ReadFile(std::addressof(read_size), file, file_offset, reinterpret_cast<void *>(load_address), file_size));
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
/* Uncompress if necessary. */
if (is_compressed) {
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
R_SUCCEED_IF(!is_compressed);
auto compressed_data_buf = reinterpret_cast<const void *>(load_address);
if (is_zstd) {
bool decompressed = util::DecompressZstdForLoader(reinterpret_cast<void *>(g_zstd_dctx_workspace), ZstdDctxWorkspaceSize, reinterpret_cast<void *>(map_base), static_cast<size_t>(map_end - map_base), segment_size, compressed_data_buf, file_size);
R_UNLESS(decompressed, ldr::ResultInvalidNso());
} else {
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment_size, compressed_data_buf, file_size) == static_cast<int>(segment_size));
R_UNLESS(decompressed, ldr::ResultInvalidNso());
}
/* Check hash if necessary. */
if (check_hash) {
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
}
R_SUCCEED();
}
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
Result CheckSegmentHash(const NsoHeader *nso_header, uintptr_t map_address, NsoHeader::Segment segment) {
if ((nso_header->flags & (NsoHeader::Flag_CheckHashText << segment)) == 0) {
R_SUCCEED();
}
u8 hash[crypto::Sha256Generator::HashSize];
crypto::GenerateSha256(hash, sizeof(hash),
reinterpret_cast<void *>(map_address + nso_header->segments[segment].dst_offset),
nso_header->segments[segment].size);
R_UNLESS(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso());
R_SUCCEED();
}
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, size_t map_size) {
const bool is_zstd = (nso_header->flags & NsoHeader::Flag_UseZbicCompression) != 0;
/* Map and read data from file. */
{
/* Map the process memory. */
void *mapped_memory = nullptr;
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, map_size, GenerateSecureRandom));
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, map_size); };
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
const uintptr_t map_end = map_address + map_size;
/* Load NSO segments. */
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Text].file_offset, nso_header->text_compressed_size, nso_header->text_size,
(nso_header->flags & NsoHeader::Flag_CompressedText) != 0, is_zstd, map_address + nso_header->text_dst_offset, map_end));
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Ro].file_offset, nso_header->ro_compressed_size, nso_header->ro_size,
(nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, is_zstd, map_address + nso_header->ro_dst_offset, map_end));
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Rw].file_offset, nso_header->rw_compressed_size, nso_header->rw_size,
(nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, is_zstd, map_address + nso_header->rw_dst_offset, map_end));
/* Clear unused space to zero. */
const size_t text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
const size_t ro_end = static_cast<size_t>(nso_header->ro_dst_offset) + static_cast<size_t>(nso_header->ro_size);
const size_t rw_end = static_cast<size_t>(nso_header->rw_dst_offset) + static_cast<size_t>(nso_header->rw_size);
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_size - rw_end);
/* Check segment hashes. */
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Text));
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Ro));
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Rw));
/* Apply embedded patches. */
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
@@ -691,25 +729,31 @@ namespace ams::ldr {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
}
if (ro_size) {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
}
if (rw_size) {
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
}
R_SUCCEED();
}
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
Result LoadAutoLoadModules(const ProcessInfo *process_info, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
/* Load each NSO. */
for (size_t i = 0; i < Nso_Count; i++) {
if (ali->has_nso[i]) {
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
const uintptr_t total_end = process_info->code_address + process_info->total_size;
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
}
for (int i = 0; i < ctx.nso_count; i++) {
const NsoIndex nso_idx = static_cast<NsoIndex>(ctx.ali.nso_indices[i]);
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(nso_idx), fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
const bool is_zstd = (ctx.headers[i].flags & NsoHeader::Flag_UseZbicCompression) != 0;
const size_t map_size = is_zstd ? (total_end - process_info->nso_address[i]) : process_info->nso_size[i];
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, ctx.headers + i,
process_info->nso_address[i], process_info->nso_size[i], map_size));
}
/* Load arguments, if present. */
@@ -735,13 +779,13 @@ namespace ams::ldr {
R_SUCCEED();
}
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
/* Get CreateProcessParameter. */
svc::CreateProcessParameter param;
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
/* Decide on an NSO layout. */
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, ali, argument));
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), ctx, argument));
/* Actually create process. */
svc::Handle process_handle;
@@ -749,10 +793,11 @@ namespace ams::ldr {
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
out->process_handle = process_handle;
out->code_address = param.code_address;
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
/* Load all auto load modules. */
R_RETURN(LoadAutoLoadModules(out, nso_headers, ali, argument));
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
}
}
@@ -793,13 +838,13 @@ namespace ams::ldr {
}
/* Load, validate NSO headers. */
AutoLoadModuleInfo auto_load_info = {};
R_TRY(LoadAutoLoadHeaders(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
R_TRY(CheckAutoLoad(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
AutoLoadModuleContext ctx;
R_TRY(LoadAutoLoadHeaders(ctx, meta.acid->flags));
R_TRY(CheckAutoLoad(ctx, meta.acid->flags));
/* Actually create the process and load NSOs into process memory. */
ProcessInfo info;
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, std::addressof(auto_load_info), argument, flags, resource_limit));
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), ctx, argument, flags, resource_limit));
/* Register NSOs with the RoManager. */
{
@@ -811,10 +856,8 @@ namespace ams::ldr {
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
/* Register all NSOs. */
for (size_t i = 0; i < Nso_Count; i++) {
if (auto_load_info.has_nso[i]) {
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
}
for (int i = 0; i < ctx.nso_count; i++) {
RoManager::GetInstance().AddNso(pin_id, ctx.headers[i].module_id, info.nso_address[i], info.nso_size[i]);
}
}

View File

@@ -15,7 +15,7 @@
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["erpt:c", "fatal:u", "fsp-srv", "ldr:shel", "lm", "lr", "pm:shell", "set", "set:sys"],
"service_access": ["fatal:u", "fsp-srv", "ldr:shel", "lr", "pm:shell", "set:sys"],
"service_host": ["pgl"],
"kernel_capabilities": [{
"type": "kernel_flags",
@@ -87,4 +87,4 @@
"type": "handle_table_size",
"value": 256
}]
}
}

View File

@@ -30,13 +30,14 @@ namespace ams {
fs::InitializeForSystem();
fs::SetAllocator(pgl::srv::Allocate, pgl::srv::Deallocate);
fs::SetEnabledAutoAbort(false);
/* Initialize lr. */
lr::Initialize();
/* Initialize other services we need. */
R_ABORT_UNLESS(setInitialize());
R_ABORT_UNLESS(setsysInitialize());
R_ABORT_UNLESS(pmshellInitialize());
R_ABORT_UNLESS(ldrShellInitialize());
R_ABORT_UNLESS(lrInitialize());
/* Verify that we can sanely execute. */
ams::CheckApiVersion();
@@ -58,3 +59,36 @@ namespace ams {
}
}
/* Override operator new. */
void *operator new(size_t size) {
return ams::pgl::srv::Allocate(size);
}
void *operator new(size_t size, const std::nothrow_t &) {
return ams::pgl::srv::Allocate(size);
}
void operator delete(void *p) {
return ams::pgl::srv::Deallocate(p, 0);
}
void operator delete(void *p, size_t size) {
return ams::pgl::srv::Deallocate(p, size);
}
void *operator new[](size_t size) {
return ams::pgl::srv::Allocate(size);
}
void *operator new[](size_t size, const std::nothrow_t &) {
return ams::pgl::srv::Allocate(size);
}
void operator delete[](void *p) {
return ams::pgl::srv::Deallocate(p, 0);
}
void operator delete[](void *p, size_t size) {
return ams::pgl::srv::Deallocate(p, size);
}

View File

@@ -344,7 +344,7 @@ def get_full(nxo):
full = put_qword(full, offset, addend + LOAD_BASE)
else:
print('TODO r_type %d' % (r_type,))
with open('E:\\full.bin', 'wb') as f:
with open('full.bin', 'wb') as f:
f.write(full)
return full
@@ -415,10 +415,14 @@ def find_types(full, num_fields):
KNOWN = range(10) + [4, 4, 2, 4]
KNOWN_OLD = range(10) + [4, 4, 0, 4]
try:
ind = full.index(''.join(pk('<I', i) for i in KNOWN))
ind = full.index(''.join(pk('<B', i) for i in KNOWN))
return list(up('<'+'B'*num_fields, full[ind:ind+num_fields]))
except ValueError:
ind = full.index(''.join(pk('<I', i) for i in KNOWN_OLD))
return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
try:
ind = full.index(''.join(pk('<I', i) for i in KNOWN))
except ValueError:
ind = full.index(''.join(pk('<I', i) for i in KNOWN_OLD))
return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields]))
def find_flags(full, num_fields, magic_idx):
KNOWN = '\x00' + ('\x01'*6) + '\x00\x01\x01\x00'