Compare commits
70 Commits
1.10.0-pre
...
1.11.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d04c20a049 | ||
|
|
252f8685b4 | ||
|
|
020ed307b1 | ||
|
|
d9e1d799ab | ||
|
|
80bd459516 | ||
|
|
931e3c37fd | ||
|
|
4b4db91341 | ||
|
|
e3fc339fe5 | ||
|
|
a051c3c723 | ||
|
|
9a1ea02c8c | ||
|
|
bae1ad22ec | ||
|
|
cd1503a9ca | ||
|
|
72e3b0dd34 | ||
|
|
ac120cf84b | ||
|
|
97ffad2ab6 | ||
|
|
c8d68a3a8a | ||
|
|
8987d642d1 | ||
|
|
7ffb1da2ac | ||
|
|
5c426bf90d | ||
|
|
31ca20f3b7 | ||
|
|
174da786e4 | ||
|
|
93a82c0441 | ||
|
|
082115187a | ||
|
|
db388385b0 | ||
|
|
4a8f7628c2 | ||
|
|
9d16ee6a74 | ||
|
|
3b9ee08c69 | ||
|
|
2694f31eb3 | ||
|
|
f3f1fa46ed | ||
|
|
8a7c2872c3 | ||
|
|
6acdc05c89 | ||
|
|
32bb6b17ab | ||
|
|
27455329b3 | ||
|
|
7d334f09ee | ||
|
|
8c0ff851f2 | ||
|
|
b108318996 | ||
|
|
5dfdc6e8b0 | ||
|
|
14e63cb04c | ||
|
|
5dd9816e3c | ||
|
|
5a5d9b206b | ||
|
|
49e2330ed8 | ||
|
|
1847db06f8 | ||
|
|
f028802fb8 | ||
|
|
b29dbeae3d | ||
|
|
4e653f67e5 | ||
|
|
a985bdb1d4 | ||
|
|
5b0a4830d4 | ||
|
|
26990b3be9 | ||
|
|
76bceffbd5 | ||
|
|
0ee6277be9 | ||
|
|
9cc82c6f80 | ||
|
|
6b831406d6 | ||
|
|
23ebd4d677 | ||
|
|
00f987dd38 | ||
|
|
f8a5a6c015 | ||
|
|
61ac03e22d | ||
|
|
208b60696c | ||
|
|
dfb936ed11 | ||
|
|
5056ab21af | ||
|
|
eb34f9789c | ||
|
|
1e88f37892 | ||
|
|
cd72f9f33e | ||
|
|
21c0f75a29 | ||
|
|
3cb5d5f957 | ||
|
|
00d1cdb533 | ||
|
|
d9fc6e99eb | ||
|
|
c08a13a546 | ||
|
|
b5b6189c85 | ||
|
|
28a378ca0d | ||
|
|
540d00e097 |
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
6
emummc/.gitrepo
vendored
6
emummc/.gitrepo
vendored
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emummc
|
||||
branch = develop
|
||||
commit = 3c57b20ba3820ec87d7dd239d6fcf9ba97510606
|
||||
parent = d61ee942d9b34cadd80464d5d549c5e2e5bf1689
|
||||
commit = 3726bfd659600cdafd138277054568a3edba60a6
|
||||
parent = 80bd459516e813fc6f10268ca31dd72a053a4ef3
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
cmdver = 0.4.9
|
||||
|
||||
2
emummc/README.md
vendored
2
emummc/README.md
vendored
@@ -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
|
||||
|
||||
16
emummc/source/FS/FS_offsets.c
vendored
16
emummc/source/FS/FS_offsets.c
vendored
@@ -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);
|
||||
}
|
||||
|
||||
6
emummc/source/FS/FS_versions.h
vendored
6
emummc/source/FS/FS_versions.h
vendored
@@ -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
59
emummc/source/FS/offsets/2120.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_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
59
emummc/source/FS/offsets/2120_exfat.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_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
59
emummc/source/FS/offsets/2200.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_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
59
emummc/source/FS/offsets/2200_exfat.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_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__
|
||||
@@ -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. */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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] = {};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 6e2c09c795c3996397b2c5720007453b782e7db9
|
||||
parent = 4928bcb00322a17345521f27f30bafa3e58ea681
|
||||
commit = 82f1553c4c7e68364f7e630b1c68f2aee8681dee
|
||||
parent = 252f8685b493d0dfd428e9439b0296109776b935
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
cmdver = 0.4.9
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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'>;
|
||||
|
||||
@@ -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(); }
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -361,10 +361,18 @@ namespace ams::kern::board::nintendo::nx {
|
||||
}();
|
||||
|
||||
/* Return (possibly) adjusted size. */
|
||||
/* NOTE: On 20.0.0+ the browser requires much more memory in the applet pool in order to function. */
|
||||
/* NOTE: On 20.0.0+ (and even more-so 21.0.0+) the browser requires much more memory in the applet pool in order to function. */
|
||||
/* Thus, we have to reduce our extra system memory size by 26 MB to compensate. */
|
||||
const size_t ExtraSystemMemoryForAtmosphere = kern::GetTargetFirmware() >= ams::TargetFirmware_20_0_0 ? 14_MB : 40_MB;
|
||||
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
|
||||
if (kern::GetTargetFirmware() >= ams::TargetFirmware_21_0_0) {
|
||||
constexpr size_t ExtraSystemMemoryForAtmosphere_21_0_0 = 7_MB;
|
||||
return base_pool_size - ExtraSystemMemoryForAtmosphere_21_0_0 - KTraceBufferSize;
|
||||
} else if (kern::GetTargetFirmware() >= ams::TargetFirmware_20_0_0) {
|
||||
constexpr size_t ExtraSystemMemoryForAtmosphere_20_0_0 = 14_MB;
|
||||
return base_pool_size - ExtraSystemMemoryForAtmosphere_20_0_0 - KTraceBufferSize;
|
||||
} else {
|
||||
constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB;
|
||||
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); };
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) \
|
||||
|
||||
@@ -97,6 +97,11 @@ namespace ams::hos {
|
||||
Version_20_4_0 = ::ams::TargetFirmware_20_4_0,
|
||||
Version_20_5_0 = ::ams::TargetFirmware_20_5_0,
|
||||
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_22_1_0 = ::ams::TargetFirmware_22_1_0,
|
||||
|
||||
Version_Current = ::ams::TargetFirmware_Current,
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -19,5 +19,6 @@
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
Result SubmitFsInfo();
|
||||
void ClearFsInfo();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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>);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
52650
libraries/libstratosphere/source/util/zstd.c
Normal file
52650
libraries/libstratosphere/source/util/zstd.c
Normal file
File diff suppressed because it is too large
Load Diff
3209
libraries/libstratosphere/source/util/zstd.h
Normal file
3209
libraries/libstratosphere/source/util/zstd.h
Normal file
File diff suppressed because it is too large
Load Diff
107
libraries/libstratosphere/source/util/zstd_errors.h
Normal file
107
libraries/libstratosphere/source/util/zstd_errors.h
Normal 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 */
|
||||
@@ -16,11 +16,11 @@
|
||||
#pragma once
|
||||
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 10
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 11
|
||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
||||
|
||||
#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 0
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 22
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1
|
||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
||||
|
||||
@@ -95,8 +95,13 @@
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_20_4_0 ATMOSPHERE_TARGET_FIRMWARE(20, 4, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_20_5_0 ATMOSPHERE_TARGET_FIRMWARE(20, 5, 0)
|
||||
#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_22_1_0 ATMOSPHERE_TARGET_FIRMWARE(22, 1, 0)
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_21_0_0
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_22_1_0
|
||||
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||
@@ -184,6 +189,11 @@ namespace ams {
|
||||
TargetFirmware_20_4_0 = ATMOSPHERE_TARGET_FIRMWARE_20_4_0,
|
||||
TargetFirmware_20_5_0 = ATMOSPHERE_TARGET_FIRMWARE_20_5_0,
|
||||
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_22_1_0 = ATMOSPHERE_TARGET_FIRMWARE_22_1_0,
|
||||
|
||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
24
stratosphere/loader/source/ldr_embedded_am_patches.inc
Normal file
24
stratosphere/loader/source/ldr_embedded_am_patches.inc
Normal 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 */
|
||||
};
|
||||
@@ -74,9 +74,14 @@ 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[] = {
|
||||
@@ -93,5 +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.1.1 */
|
||||
{ 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 */
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 we have rtld, we must also 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 only have one of [subsdk, browser dll]. */
|
||||
R_UNLESS(!(ali->has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* If we don't have rtld, we are not browser, and 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -53,14 +53,14 @@ namespace ams::ro::impl {
|
||||
|
||||
Result SetNroPerms(os::NativeHandle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size, bool is_aligned_header) {
|
||||
const u64 rx_offset = is_aligned_header ? os::MemoryPageSize : 0;
|
||||
const u64 ro_offset = rx_offset + rx_size;
|
||||
const u64 ro_offset = rx_size;
|
||||
const u64 rw_offset = ro_offset + ro_size;
|
||||
|
||||
if (is_aligned_header) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address, os::MemoryPageSize, os::MemoryPermission_ReadOnly));
|
||||
}
|
||||
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, os::MemoryPermission_ReadExecute));
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size - rx_offset, os::MemoryPermission_ReadExecute));
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user