Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dbc79dd5c | ||
|
|
90b54c03b3 | ||
|
|
7821241356 | ||
|
|
8fea8d9b2e | ||
|
|
dcdf46f576 | ||
|
|
df5537b748 | ||
|
|
0f2855ada8 | ||
|
|
c790d03693 | ||
|
|
25be7c5b1b | ||
|
|
7e05e12b83 | ||
|
|
99d7f72c51 | ||
|
|
691a453d77 | ||
|
|
88246f475c | ||
|
|
269d4496b2 | ||
|
|
bb4c7a390b | ||
|
|
b846628362 | ||
|
|
26fb201518 | ||
|
|
01ce7cef14 | ||
|
|
3f3aaa01fa | ||
|
|
bf8de39e69 | ||
|
|
6bb4253df5 | ||
|
|
cfd7121574 | ||
|
|
972681c57e | ||
|
|
0a11cbc2d6 | ||
|
|
32f487abfb | ||
|
|
7d61cab01c | ||
|
|
14ed4e4057 | ||
|
|
e8ba632606 | ||
|
|
dbcb1e1564 | ||
|
|
15381409dc | ||
|
|
10ad6934ac | ||
|
|
03e66efd85 | ||
|
|
7040e8976d | ||
|
|
296a6af058 | ||
|
|
2c332d1cf8 | ||
|
|
243d7dc777 | ||
|
|
355010ad84 | ||
|
|
ef0c15b764 | ||
|
|
0dc308d92a | ||
|
|
eb5542963f | ||
|
|
18673d96cb | ||
|
|
36f6bdc3a0 | ||
|
|
d6fff49845 | ||
|
|
d05e8fb23a | ||
|
|
0767d9f8da | ||
|
|
21f3d29df7 | ||
|
|
4f16106702 | ||
|
|
19be54ff95 | ||
|
|
ed80d6ec8c | ||
|
|
57b6c71c1c | ||
|
|
0a11d341b7 | ||
|
|
8010290472 | ||
|
|
fbc526d163 | ||
|
|
5bb790e4a7 | ||
|
|
0a6219e6e0 | ||
|
|
037b04ac60 | ||
|
|
9e563d590b | ||
|
|
bdcf02a3ef | ||
|
|
88ac85c423 | ||
|
|
2e1a93f1d1 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -49,6 +49,8 @@ X.X.X</br>
|
||||
- [ Ex: Kosmos' distribution of Atmosphère ]
|
||||
- Do you have additional kips or sysmodules you're loading:
|
||||
- Homebrew software installed: [ * ]
|
||||
- EmuMMC or SysNAND:
|
||||
- [ If using an EmuMMC, include whether it's partition-based or file-based. ]
|
||||
|
||||
### Additional context?
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,30 @@
|
||||
# Changelog
|
||||
## 0.19.4
|
||||
+ Support was added for 12.0.3.
|
||||
+ A number of minor issues were fixed, including:
|
||||
+ An issue was fixed that could cause heap memory corruption when allocation was highly contended.
|
||||
+ An issue was fixed that could cause sleep to fail under certain conditions.
|
||||
+ An issue was fixed that could cause a scheduler slow path to be taken more often than necessary.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.19.3
|
||||
+ Support was added for 12.0.2.
|
||||
+ A number of minor issues were fixed, including:
|
||||
+ An issue was fixed in dns.mitm that caused a crash when games attempted to resolve the IP address of nullptr.
|
||||
+ An issue was fixed in erpt that would cause an abort when booting without having ever booted stock previously.
|
||||
+ An issue was fixed in (file-based) emummc that caused an error on system format/downloading certain games.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.19.2
|
||||
+ Atmosphère's components were further updated to reflect latest official behaviors as of 12.0.0.
|
||||
+ Notably, `erpt` was updated to implement the new forced shutdown detection feature.
|
||||
+ When a forced-shutdown occurs, an erpt_report will be generated and saved to the SD card on the next boot.
|
||||
+ Atmosphere-libs was updated to use GCC 11 (latest devkitA64/devkitARM releases).
|
||||
+ Initial inspections show mild-to-moderate optimizer improvements in several important places (kernel is 0x3000 smaller).
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
+ A number of minor issues were fixed, including:
|
||||
+ A bug was fixed that caused a black screen when attempting to boot firmware versions 2.0.0-4.1.0.
|
||||
+ A bug was fixed that caused sm to abort when at the session limit, rather than returning error codes.
|
||||
+ A bug was fixed that allowed for resource exhaustion on 12.0.0, under certain circumstances.
|
||||
+ Several issues were fixed, and usability and stability were improved.
|
||||
## 0.19.1
|
||||
+ An issue was fixed that caused a fatal error when using official `migration` services to transfer data between consoles.
|
||||
+ An issue was fixed in `ncm` that caused an error when the OS tried to enumerate installed SD card content.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emuMMC
|
||||
branch = develop
|
||||
commit = b355ee6a8f376faa615785419c7d73a8814d9d65
|
||||
parent = b24784f5c13a142bd0cb5d7edb82691c71f4bd00
|
||||
commit = 219c723c3001fbc33d47840ceceb83cf39a1d218
|
||||
parent = 7821241356c2f6b159945babf657ffd921957918
|
||||
method = rebase
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
#include "offsets/1100_exfat.h"
|
||||
#include "offsets/1200.h"
|
||||
#include "offsets/1200_exfat.h"
|
||||
#include "offsets/1203.h"
|
||||
#include "offsets/1203_exfat.h"
|
||||
#include "../utils/fatal.h"
|
||||
|
||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
||||
@@ -117,6 +119,8 @@ DEFINE_OFFSET_STRUCT(_1100);
|
||||
DEFINE_OFFSET_STRUCT(_1100_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1200);
|
||||
DEFINE_OFFSET_STRUCT(_1200_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1203);
|
||||
DEFINE_OFFSET_STRUCT(_1203_EXFAT);
|
||||
|
||||
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
switch (version) {
|
||||
@@ -194,6 +198,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1200));
|
||||
case FS_VER_12_0_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1200_EXFAT));
|
||||
case FS_VER_12_0_3:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1203));
|
||||
case FS_VER_12_0_3_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1203_EXFAT));
|
||||
default:
|
||||
fatal_abort(Fatal_UnknownVersion);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ enum FS_VER
|
||||
FS_VER_12_0_0,
|
||||
FS_VER_12_0_0_EXFAT,
|
||||
|
||||
FS_VER_12_0_3,
|
||||
FS_VER_12_0_3_EXFAT,
|
||||
|
||||
FS_VER_MAX,
|
||||
};
|
||||
|
||||
|
||||
60
emummc/source/FS/offsets/1203.h
Normal file
60
emummc/source/FS/offsets/1203.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
* Copyright (c) 2021 CTCaer
|
||||
*
|
||||
* 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_1203_H__
|
||||
#define __FS_1203_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1203_SDMMC_ACCESSOR_GC 0x1550E0
|
||||
#define FS_OFFSET_1203_SDMMC_ACCESSOR_SD 0x156EF0
|
||||
#define FS_OFFSET_1203_SDMMC_ACCESSOR_NAND 0x155610
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1203_SDMMC_WRAPPER_READ 0x150A80
|
||||
#define FS_OFFSET_1203_SDMMC_WRAPPER_WRITE 0x150B40
|
||||
#define FS_OFFSET_1203_RTLD 0x688
|
||||
#define FS_OFFSET_1203_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1203_CLKRST_SET_MIN_V_CLK_RATE 0x14FDD0
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1203_LOCK_MUTEX 0x29350
|
||||
#define FS_OFFSET_1203_UNLOCK_MUTEX 0x293A0
|
||||
|
||||
#define FS_OFFSET_1203_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150960
|
||||
#define FS_OFFSET_1203_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1509F0
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1203_SD_MUTEX 0xE3D3E8
|
||||
#define FS_OFFSET_1203_NAND_MUTEX 0xE38768
|
||||
#define FS_OFFSET_1203_ACTIVE_PARTITION 0xE387A8
|
||||
#define FS_OFFSET_1203_SDMMC_DAS_HANDLE 0xE20DB0
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1203_SD_DAS_INIT 0x27244
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1203_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006E920, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0007AFD0, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00081364, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00092960, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1203_H__
|
||||
60
emummc/source/FS/offsets/1203_exfat.h
Normal file
60
emummc/source/FS/offsets/1203_exfat.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
* Copyright (c) 2021 CTCaer
|
||||
*
|
||||
* 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_1203_EXFAT_H__
|
||||
#define __FS_1203_EXFAT_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_GC 0x1550E0
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_SD 0x156EF0
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_ACCESSOR_NAND 0x155610
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_READ 0x150A80
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_WRITE 0x150B40
|
||||
#define FS_OFFSET_1203_EXFAT_RTLD 0x688
|
||||
#define FS_OFFSET_1203_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1203_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x14FDD0
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1203_EXFAT_LOCK_MUTEX 0x29350
|
||||
#define FS_OFFSET_1203_EXFAT_UNLOCK_MUTEX 0x293A0
|
||||
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x150960
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1509F0
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1203_EXFAT_SD_MUTEX 0xE4B3E8
|
||||
#define FS_OFFSET_1203_EXFAT_NAND_MUTEX 0xE46768
|
||||
#define FS_OFFSET_1203_EXFAT_ACTIVE_PARTITION 0xE467A8
|
||||
#define FS_OFFSET_1203_EXFAT_SDMMC_DAS_HANDLE 0xE2EDB0
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1203_EXFAT_SD_DAS_INIT 0x27244
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1203_EXFAT_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006E920, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0007AFD0, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00081364, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00092960, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1203_EXFAT_H__
|
||||
@@ -292,6 +292,36 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned
|
||||
{
|
||||
fp = &f_emu.fp_gpp[sector / f_emu.part_size];
|
||||
sector = sector % f_emu.part_size;
|
||||
|
||||
// Special handling for reads/writes which cross file-boundaries.
|
||||
if (__builtin_expect(sector + num_sectors > f_emu.part_size, 0))
|
||||
{
|
||||
unsigned int remaining = num_sectors;
|
||||
while (remaining > 0) {
|
||||
const unsigned int cur_sectors = MIN(remaining, f_emu.part_size - sector);
|
||||
|
||||
if (f_lseek(fp, (u64)sector << 9) != FR_OK)
|
||||
return 0; // Out of bounds.
|
||||
|
||||
if (is_write)
|
||||
{
|
||||
if (f_write_fast(fp, buf, (u64)cur_sectors << 9) != FR_OK)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (f_read_fast(fp, buf, (u64)cur_sectors << 9) != FR_OK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = (char *)buf + ((u64)cur_sectors << 9);
|
||||
remaining -= cur_sectors;
|
||||
sector = 0;
|
||||
++fp;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -306,14 +336,14 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned
|
||||
break;
|
||||
}
|
||||
|
||||
if (f_lseek(fp, sector << 9) != FR_OK)
|
||||
if (f_lseek(fp, (u64)sector << 9) != FR_OK)
|
||||
return 0; // Out of bounds.
|
||||
|
||||
uint64_t res = 0;
|
||||
if (!is_write)
|
||||
res = !f_read_fast(fp, buf, num_sectors << 9);
|
||||
res = !f_read_fast(fp, buf, (u64)num_sectors << 9);
|
||||
else
|
||||
res = !f_write_fast(fp, buf, num_sectors << 9);
|
||||
res = !f_write_fast(fp, buf, (u64)num_sectors << 9);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /mariko_fatal.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /mariko_fatal.ld) --gc-sections --nmagic
|
||||
@@ -1,4 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic
|
||||
@@ -36,7 +36,6 @@ namespace ams::warmboot {
|
||||
void Main(const Metadata *metadata) {
|
||||
/* Ensure that we're running under vaguely sane conditions. */
|
||||
AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic);
|
||||
AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max);
|
||||
|
||||
/* Restrict the bpmp's access to dram. */
|
||||
if (metadata->target_firmware >= TargetFirmware_4_0_0) {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
||||
%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic
|
||||
@@ -46,6 +46,9 @@ CFLAGS := \
|
||||
-std=gnu11 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread \
|
||||
-fstrict-volatile-bitfields \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ CFLAGS := \
|
||||
-std=gnu11 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread \
|
||||
-fstrict-volatile-bitfields \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ CFLAGS := \
|
||||
-std=gnu11 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread \
|
||||
-fstrict-volatile-bitfields \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ CFLAGS := \
|
||||
-std=gnu11 \
|
||||
-Werror \
|
||||
-Wall \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread \
|
||||
-fstrict-volatile-bitfields \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ typedef enum {
|
||||
FS_VER_12_0_0,
|
||||
FS_VER_12_0_0_EXFAT,
|
||||
|
||||
FS_VER_12_0_3,
|
||||
FS_VER_12_0_3_EXFAT,
|
||||
|
||||
FS_VER_MAX,
|
||||
} emummc_fs_ver_t;
|
||||
|
||||
|
||||
@@ -348,6 +348,7 @@ uint32_t fuse_get_regulator(void) {
|
||||
|
||||
|
||||
static const uint32_t fuse_version_increment_firmwares[] = {
|
||||
ATMOSPHERE_TARGET_FIRMWARE_12_0_2,
|
||||
ATMOSPHERE_TARGET_FIRMWARE_11_0_0,
|
||||
ATMOSPHERE_TARGET_FIRMWARE_10_0_0,
|
||||
ATMOSPHERE_TARGET_FIRMWARE_9_1_0,
|
||||
|
||||
@@ -429,6 +429,9 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
|
||||
|
||||
"\xDC\x2A\x08\x49\x96\xBB\x3C\x01", /* FS_VER_12_0_0 */
|
||||
"\xD5\xA5\xBF\x36\x64\x0C\x49\xEA", /* FS_VER_12_0_0_EXFAT */
|
||||
|
||||
"\xC8\x67\x62\xBE\x19\xA5\x1F\xA0", /* FS_VER_12_0_3 */
|
||||
"\xE1\xE8\xD3\xD6\xA2\xFE\x0B\x10", /* FS_VER_12_0_3_EXFAT */
|
||||
};
|
||||
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {
|
||||
|
||||
@@ -285,7 +285,11 @@ static bool is_nca_present(const char *nca_name) {
|
||||
static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){
|
||||
#define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0)
|
||||
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_0) {
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_2) {
|
||||
CHECK_NCA("a1863a5c0e1cedd442f5e60b0422dc15", 12_0_3);
|
||||
CHECK_NCA("63d928b5a3016fe8cc0e76d2f06f4e98", 12_0_2);
|
||||
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_0) {
|
||||
CHECK_NCA("e65114b456f9d0b566a80e53bade2d89", 12_0_1);
|
||||
CHECK_NCA("bd4185843550fbba125b20787005d1d2", 12_0_0);
|
||||
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) {
|
||||
CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1);
|
||||
@@ -392,6 +396,8 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_11_0_0;
|
||||
} else if (memcmp(package1loader_header->build_timestamp, "20210129", 8) == 0) {
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_12_0_0;
|
||||
} else if (memcmp(package1loader_header->build_timestamp, "20210422", 8) == 0) {
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_12_0_2;
|
||||
} else {
|
||||
fatal_error("[NXBOOT] Unable to identify package1!\n");
|
||||
}
|
||||
@@ -603,6 +609,11 @@ static void nxboot_configure_stratosphere(uint32_t target_firmware) {
|
||||
/* NOTE: 12.0.0 added a new lotus firmware, but did not burn a fuse. */
|
||||
/* This is literally undetectable using normal fuses.... */
|
||||
/* C'est la vie. */
|
||||
|
||||
/* Check if the fuses are < 12.0.0, but firmware is >= 12.0.0 */
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_12_0_2 && !(fuse_get_reserved_odm(7) & ~0x00003FFF)) {
|
||||
kip_patches_set_enable_nogc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[]
|
||||
|
||||
/* Perform version checks. */
|
||||
/* We will be compatible with all package2s released before current, but not newer ones. */
|
||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1100_CURRENT) {
|
||||
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1202_CURRENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
#define PACKAGE2_MAXVER_900 0xC
|
||||
#define PACKAGE2_MAXVER_910_920 0xD
|
||||
#define PACKAGE2_MAXVER_1000 0xE
|
||||
#define PACKAGE2_MAXVER_1100_CURRENT 0xF
|
||||
#define PACKAGE2_MAXVER_1100 0xF
|
||||
#define PACKAGE2_MAXVER_1202_CURRENT 0x10
|
||||
|
||||
#define PACKAGE2_MINVER_100 0x3
|
||||
#define PACKAGE2_MINVER_200 0x4
|
||||
@@ -56,7 +57,8 @@
|
||||
#define PACKAGE2_MINVER_900 0xD
|
||||
#define PACKAGE2_MINVER_910_920 0xE
|
||||
#define PACKAGE2_MINVER_1000 0xF
|
||||
#define PACKAGE2_MINVER_1100_CURRENT 0x10
|
||||
#define PACKAGE2_MINVER_1100 0x10
|
||||
#define PACKAGE2_MINVER_1202_CURRENT 0x11
|
||||
|
||||
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 86c2eec8e9e966a30c19692adb79faeda45c1940
|
||||
parent = aa2d03d8e13bc5d3f34751b6105503a601dc958e
|
||||
commit = b5b55f60d8567ea4608163abc299e5e238381d44
|
||||
parent = 7e05e12b833e19333ffac02e4fec72250c8b17fa
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -8,13 +8,19 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64)
|
||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread
|
||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
|
||||
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
|
||||
else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm)
|
||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread
|
||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
|
||||
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
|
||||
|
||||
@@ -34,7 +34,8 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||
-Wl,--wrap,_Unwind_Resume \
|
||||
-Wl,--wrap,_ZSt19__throw_logic_errorPKc \
|
||||
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
|
||||
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
|
||||
-Wl,--wrap,_ZNSt11logic_errorC2EPKc \
|
||||
-Wl,--wrap,exit
|
||||
|
||||
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread
|
||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
|
||||
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
|
||||
@@ -16,7 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions
|
||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions \
|
||||
-Wno-array-bounds \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-stringop-overread
|
||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
|
||||
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace ams::pkg2 {
|
||||
|
||||
constexpr inline int PayloadCount = 3;
|
||||
|
||||
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x10 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0xE;
|
||||
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x11 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0xF;
|
||||
|
||||
struct Package2Meta {
|
||||
using Magic = util::FourCC<'P','K','2','1'>;
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace ams::fuse {
|
||||
}
|
||||
|
||||
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
|
||||
TargetFirmware_12_0_2,
|
||||
TargetFirmware_11_0_0,
|
||||
TargetFirmware_10_0_0,
|
||||
TargetFirmware_9_1_0,
|
||||
|
||||
@@ -140,6 +140,8 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
kern_libc_generic.o: CFLAGS += -fno-builtin
|
||||
|
||||
kern_k_auto_object.o: CXXFLAGS += -fno-lto
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_select_assembly_offsets.h>
|
||||
|
||||
namespace ams::kern::init {
|
||||
|
||||
@@ -31,5 +32,19 @@ namespace ams::kern::init {
|
||||
u64 setup_function;
|
||||
u64 exception_stack;
|
||||
};
|
||||
static_assert(sizeof(KInitArguments) == INIT_ARGUMENTS_SIZE);
|
||||
|
||||
static_assert(__builtin_offsetof(KInitArguments, ttbr0) == INIT_ARGUMENTS_TTBR0);
|
||||
static_assert(__builtin_offsetof(KInitArguments, ttbr1) == INIT_ARGUMENTS_TTBR1);
|
||||
static_assert(__builtin_offsetof(KInitArguments, tcr) == INIT_ARGUMENTS_TCR);
|
||||
static_assert(__builtin_offsetof(KInitArguments, mair) == INIT_ARGUMENTS_MAIR);
|
||||
static_assert(__builtin_offsetof(KInitArguments, cpuactlr) == INIT_ARGUMENTS_CPUACTLR);
|
||||
static_assert(__builtin_offsetof(KInitArguments, cpuectlr) == INIT_ARGUMENTS_CPUECTLR);
|
||||
static_assert(__builtin_offsetof(KInitArguments, sctlr) == INIT_ARGUMENTS_SCTLR);
|
||||
static_assert(__builtin_offsetof(KInitArguments, sp) == INIT_ARGUMENTS_SP);
|
||||
static_assert(__builtin_offsetof(KInitArguments, entrypoint) == INIT_ARGUMENTS_ENTRYPOINT);
|
||||
static_assert(__builtin_offsetof(KInitArguments, argument) == INIT_ARGUMENTS_ARGUMENT);
|
||||
static_assert(__builtin_offsetof(KInitArguments, setup_function) == INIT_ARGUMENTS_SETUP_FUNCTION);
|
||||
static_assert(__builtin_offsetof(KInitArguments, exception_stack) == INIT_ARGUMENTS_EXCEPTION_STACK);
|
||||
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* TODO: Different header for this? */
|
||||
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
|
||||
|
||||
/* ams::kern::KThread::StackParameters, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp */
|
||||
#define THREAD_STACK_PARAMETERS_SIZE 0x30
|
||||
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
||||
#define THREAD_STACK_PARAMETERS_CONTEXT 0x18
|
||||
@@ -24,4 +28,130 @@
|
||||
#define THREAD_STACK_PARAMETERS_CURRENT_SVC_ID 0x2B
|
||||
#define THREAD_STACK_PARAMETERS_IS_CALLING_SVC 0x2C
|
||||
#define THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER 0x2D
|
||||
#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E
|
||||
#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E
|
||||
|
||||
/* ams::kern::arch::arm64::KThreadContext, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp */
|
||||
#define THREAD_CONTEXT_SIZE 0x290
|
||||
#define THREAD_CONTEXT_CPU_REGISTERS 0x000
|
||||
#define THREAD_CONTEXT_X19 0x000
|
||||
#define THREAD_CONTEXT_X20 0x008
|
||||
#define THREAD_CONTEXT_X21 0x010
|
||||
#define THREAD_CONTEXT_X22 0x018
|
||||
#define THREAD_CONTEXT_X23 0x020
|
||||
#define THREAD_CONTEXT_X24 0x028
|
||||
#define THREAD_CONTEXT_X25 0x030
|
||||
#define THREAD_CONTEXT_X26 0x038
|
||||
#define THREAD_CONTEXT_X27 0x040
|
||||
#define THREAD_CONTEXT_X28 0x048
|
||||
#define THREAD_CONTEXT_X29 0x050
|
||||
#define THREAD_CONTEXT_LR 0x058
|
||||
#define THREAD_CONTEXT_SP 0x060
|
||||
#define THREAD_CONTEXT_CPACR 0x068
|
||||
#define THREAD_CONTEXT_FPCR 0x070
|
||||
#define THREAD_CONTEXT_FPSR 0x078
|
||||
#define THREAD_CONTEXT_FPU_REGISTERS 0x080
|
||||
#define THREAD_CONTEXT_LOCKED 0x280
|
||||
|
||||
#define THREAD_CONTEXT_X19_X20 THREAD_CONTEXT_X19
|
||||
#define THREAD_CONTEXT_X21_X22 THREAD_CONTEXT_X21
|
||||
#define THREAD_CONTEXT_X23_X24 THREAD_CONTEXT_X23
|
||||
#define THREAD_CONTEXT_X25_X26 THREAD_CONTEXT_X25
|
||||
#define THREAD_CONTEXT_X27_X28 THREAD_CONTEXT_X27
|
||||
#define THREAD_CONTEXT_X29_X30 THREAD_CONTEXT_X29
|
||||
#define THREAD_CONTEXT_LR_SP THREAD_CONTEXT_LR
|
||||
#define THREAD_CONTEXT_SP_CPACR THREAD_CONTEXT_SP
|
||||
#define THREAD_CONTEXT_FPCR_FPSR THREAD_CONTEXT_FPCR
|
||||
|
||||
/* ams::kern::arch::arm64::KExceptionContext, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_exception_context.hpp */
|
||||
#define EXCEPTION_CONTEXT_SIZE 0x120
|
||||
#define EXCEPTION_CONTEXT_X0 0x000
|
||||
#define EXCEPTION_CONTEXT_X1 0x008
|
||||
#define EXCEPTION_CONTEXT_X2 0x010
|
||||
#define EXCEPTION_CONTEXT_X3 0x018
|
||||
#define EXCEPTION_CONTEXT_X4 0x020
|
||||
#define EXCEPTION_CONTEXT_X5 0x028
|
||||
#define EXCEPTION_CONTEXT_X6 0x030
|
||||
#define EXCEPTION_CONTEXT_X7 0x038
|
||||
#define EXCEPTION_CONTEXT_X8 0x040
|
||||
#define EXCEPTION_CONTEXT_X9 0x048
|
||||
#define EXCEPTION_CONTEXT_X10 0x050
|
||||
#define EXCEPTION_CONTEXT_X11 0x058
|
||||
#define EXCEPTION_CONTEXT_X12 0x060
|
||||
#define EXCEPTION_CONTEXT_X13 0x068
|
||||
#define EXCEPTION_CONTEXT_X14 0x070
|
||||
#define EXCEPTION_CONTEXT_X15 0x078
|
||||
#define EXCEPTION_CONTEXT_X16 0x080
|
||||
#define EXCEPTION_CONTEXT_X17 0x088
|
||||
#define EXCEPTION_CONTEXT_X18 0x090
|
||||
#define EXCEPTION_CONTEXT_X19 0x098
|
||||
#define EXCEPTION_CONTEXT_X20 0x0A0
|
||||
#define EXCEPTION_CONTEXT_X21 0x0A8
|
||||
#define EXCEPTION_CONTEXT_X22 0x0B0
|
||||
#define EXCEPTION_CONTEXT_X23 0x0B8
|
||||
#define EXCEPTION_CONTEXT_X24 0x0C0
|
||||
#define EXCEPTION_CONTEXT_X25 0x0C8
|
||||
#define EXCEPTION_CONTEXT_X26 0x0D0
|
||||
#define EXCEPTION_CONTEXT_X27 0x0D8
|
||||
#define EXCEPTION_CONTEXT_X28 0x0E0
|
||||
#define EXCEPTION_CONTEXT_X29 0x0E8
|
||||
#define EXCEPTION_CONTEXT_X30 0x0F0
|
||||
#define EXCEPTION_CONTEXT_SP 0x0F8
|
||||
#define EXCEPTION_CONTEXT_PC 0x100
|
||||
#define EXCEPTION_CONTEXT_PSR 0x108
|
||||
#define EXCEPTION_CONTEXT_TPIDR 0x110
|
||||
|
||||
#define EXCEPTION_CONTEXT_X0_X1 EXCEPTION_CONTEXT_X0
|
||||
#define EXCEPTION_CONTEXT_X2_X3 EXCEPTION_CONTEXT_X2
|
||||
#define EXCEPTION_CONTEXT_X4_X5 EXCEPTION_CONTEXT_X4
|
||||
#define EXCEPTION_CONTEXT_X6_X7 EXCEPTION_CONTEXT_X6
|
||||
#define EXCEPTION_CONTEXT_X8_X9 EXCEPTION_CONTEXT_X8
|
||||
#define EXCEPTION_CONTEXT_X10_X11 EXCEPTION_CONTEXT_X10
|
||||
#define EXCEPTION_CONTEXT_X12_X13 EXCEPTION_CONTEXT_X12
|
||||
#define EXCEPTION_CONTEXT_X14_X15 EXCEPTION_CONTEXT_X14
|
||||
#define EXCEPTION_CONTEXT_X16_X17 EXCEPTION_CONTEXT_X16
|
||||
#define EXCEPTION_CONTEXT_X18_X19 EXCEPTION_CONTEXT_X18
|
||||
#define EXCEPTION_CONTEXT_X20_X21 EXCEPTION_CONTEXT_X20
|
||||
#define EXCEPTION_CONTEXT_X22_X23 EXCEPTION_CONTEXT_X22
|
||||
#define EXCEPTION_CONTEXT_X24_X25 EXCEPTION_CONTEXT_X24
|
||||
#define EXCEPTION_CONTEXT_X26_X27 EXCEPTION_CONTEXT_X26
|
||||
#define EXCEPTION_CONTEXT_X28_X29 EXCEPTION_CONTEXT_X28
|
||||
#define EXCEPTION_CONTEXT_X30_SP EXCEPTION_CONTEXT_X30
|
||||
#define EXCEPTION_CONTEXT_PC_PSR EXCEPTION_CONTEXT_PC
|
||||
|
||||
#define EXCEPTION_CONTEXT_X9_X10 EXCEPTION_CONTEXT_X9
|
||||
#define EXCEPTION_CONTEXT_X19_X20 EXCEPTION_CONTEXT_X19
|
||||
#define EXCEPTION_CONTEXT_X21_X22 EXCEPTION_CONTEXT_X21
|
||||
#define EXCEPTION_CONTEXT_X23_X24 EXCEPTION_CONTEXT_X23
|
||||
#define EXCEPTION_CONTEXT_X25_X26 EXCEPTION_CONTEXT_X25
|
||||
#define EXCEPTION_CONTEXT_X27_X28 EXCEPTION_CONTEXT_X27
|
||||
#define EXCEPTION_CONTEXT_X29_X30 EXCEPTION_CONTEXT_X29
|
||||
#define EXCEPTION_CONTEXT_SP_PC EXCEPTION_CONTEXT_SP
|
||||
#define EXCEPTION_CONTEXT_PSR_TPIDR EXCEPTION_CONTEXT_PSR
|
||||
|
||||
/* ams::svc::arch::arm64::ThreadLocalRegion, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libvapours/include/vapours/svc/arch/arm64/svc_thread_local_region.hpp */
|
||||
#define THREAD_LOCAL_REGION_MESSAGE_BUFFER 0x000
|
||||
#define THREAD_LOCAL_REGION_DISABLE_COUNT 0x100
|
||||
#define THREAD_LOCAL_REGION_INTERRUPT_FLAG 0x102
|
||||
#define THREAD_LOCAL_REGION_SIZE 0x200
|
||||
|
||||
/* ams::kern::init::KInitArguments, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_arguments.hpp */
|
||||
#define INIT_ARGUMENTS_SIZE 0x60
|
||||
#define INIT_ARGUMENTS_TTBR0 0x00
|
||||
#define INIT_ARGUMENTS_TTBR1 0x08
|
||||
#define INIT_ARGUMENTS_TCR 0x10
|
||||
#define INIT_ARGUMENTS_MAIR 0x18
|
||||
#define INIT_ARGUMENTS_CPUACTLR 0x20
|
||||
#define INIT_ARGUMENTS_CPUECTLR 0x28
|
||||
#define INIT_ARGUMENTS_SCTLR 0x30
|
||||
#define INIT_ARGUMENTS_SP 0x38
|
||||
#define INIT_ARGUMENTS_ENTRYPOINT 0x40
|
||||
#define INIT_ARGUMENTS_ARGUMENT 0x48
|
||||
#define INIT_ARGUMENTS_SETUP_FUNCTION 0x50
|
||||
#define INIT_ARGUMENTS_EXCEPTION_STACK 0x58
|
||||
|
||||
/* ams::kern::KScheduler (::SchedulingState), https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp */
|
||||
/* NOTE: Due to constraints on ldarb relative offsets, KSCHEDULER_NEEDS_SCHEDULING cannot trivially be changed, and will require assembly edits. */
|
||||
#define KSCHEDULER_NEEDS_SCHEDULING 0x00
|
||||
#define KSCHEDULER_INTERRUPT_TASK_THREAD_RUNNABLE 0x01
|
||||
#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x10
|
||||
#define KSCHEDULER_IDLE_THREAD_STACK 0x18
|
||||
|
||||
@@ -43,6 +43,42 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(KExceptionContext) == 0x120);
|
||||
static_assert(sizeof(KExceptionContext) == EXCEPTION_CONTEXT_SIZE);
|
||||
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 0]) == EXCEPTION_CONTEXT_X0);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 1]) == EXCEPTION_CONTEXT_X1);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 2]) == EXCEPTION_CONTEXT_X2);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 3]) == EXCEPTION_CONTEXT_X3);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 4]) == EXCEPTION_CONTEXT_X4);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 5]) == EXCEPTION_CONTEXT_X5);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 6]) == EXCEPTION_CONTEXT_X6);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 7]) == EXCEPTION_CONTEXT_X7);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 8]) == EXCEPTION_CONTEXT_X8);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[ 9]) == EXCEPTION_CONTEXT_X9);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[10]) == EXCEPTION_CONTEXT_X10);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[11]) == EXCEPTION_CONTEXT_X11);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[12]) == EXCEPTION_CONTEXT_X12);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[13]) == EXCEPTION_CONTEXT_X13);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[14]) == EXCEPTION_CONTEXT_X14);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[15]) == EXCEPTION_CONTEXT_X15);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[16]) == EXCEPTION_CONTEXT_X16);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[17]) == EXCEPTION_CONTEXT_X17);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[18]) == EXCEPTION_CONTEXT_X18);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[19]) == EXCEPTION_CONTEXT_X19);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[20]) == EXCEPTION_CONTEXT_X20);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[21]) == EXCEPTION_CONTEXT_X21);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[22]) == EXCEPTION_CONTEXT_X22);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[23]) == EXCEPTION_CONTEXT_X23);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[24]) == EXCEPTION_CONTEXT_X24);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[25]) == EXCEPTION_CONTEXT_X25);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[26]) == EXCEPTION_CONTEXT_X26);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[27]) == EXCEPTION_CONTEXT_X27);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[28]) == EXCEPTION_CONTEXT_X28);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[29]) == EXCEPTION_CONTEXT_X29);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, x[30]) == EXCEPTION_CONTEXT_X30);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, sp) == EXCEPTION_CONTEXT_SP);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, pc) == EXCEPTION_CONTEXT_PC);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, psr) == EXCEPTION_CONTEXT_PSR);
|
||||
static_assert(__builtin_offsetof(KExceptionContext, tpidr) == EXCEPTION_CONTEXT_TPIDR);
|
||||
|
||||
}
|
||||
@@ -79,8 +79,38 @@ namespace ams::kern::arch::arm64 {
|
||||
const u128 *GetFpuRegisters() const { return m_fpu_registers; }
|
||||
public:
|
||||
static void OnThreadTerminating(const KThread *thread);
|
||||
public:
|
||||
static consteval bool ValidateOffsets();
|
||||
};
|
||||
|
||||
consteval bool KThreadContext::ValidateOffsets() {
|
||||
static_assert(sizeof(KThreadContext) == THREAD_CONTEXT_SIZE);
|
||||
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.registers) == THREAD_CONTEXT_CPU_REGISTERS);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x19) == THREAD_CONTEXT_X19);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x20) == THREAD_CONTEXT_X20);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x21) == THREAD_CONTEXT_X21);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x22) == THREAD_CONTEXT_X22);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x23) == THREAD_CONTEXT_X23);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x24) == THREAD_CONTEXT_X24);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x25) == THREAD_CONTEXT_X25);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x26) == THREAD_CONTEXT_X26);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x27) == THREAD_CONTEXT_X27);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x28) == THREAD_CONTEXT_X28);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_callee_saved.x29) == THREAD_CONTEXT_X29);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_lr) == THREAD_CONTEXT_LR);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_sp) == THREAD_CONTEXT_SP);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_cpacr) == THREAD_CONTEXT_CPACR);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_fpcr) == THREAD_CONTEXT_FPCR);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_fpsr) == THREAD_CONTEXT_FPSR);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_fpu_registers) == THREAD_CONTEXT_FPU_REGISTERS);
|
||||
static_assert(__builtin_offsetof(KThreadContext, m_locked) == THREAD_CONTEXT_LOCKED);
|
||||
|
||||
return true;
|
||||
}
|
||||
static_assert(KThreadContext::ValidateOffsets());
|
||||
|
||||
|
||||
void GetUserContext(ams::svc::ThreadContext *out, const KThread *thread);
|
||||
|
||||
}
|
||||
@@ -37,4 +37,6 @@ namespace ams::kern {
|
||||
KVirtualAddress GetInitialProcessBinaryAddress();
|
||||
size_t GetInitialProcessesSecureMemorySize();
|
||||
|
||||
void LoadInitialProcessBinaryHeaderDeprecated(KPhysicalAddress pool_end);
|
||||
|
||||
}
|
||||
|
||||
@@ -121,36 +121,8 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
NOINLINE bool Open() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Atomically increment the reference count, only if it's positive. */
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (AMS_UNLIKELY(cur_ref_count == 0)) {
|
||||
MESOSPHERE_AUDIT(cur_ref_count != 0);
|
||||
return false;
|
||||
}
|
||||
MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void Close() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Atomically decrement the reference count, not allowing it to become negative. */
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
|
||||
do {
|
||||
MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed));
|
||||
|
||||
/* If ref count hits zero, schedule the object for destruction. */
|
||||
if (cur_ref_count - 1 == 0) {
|
||||
this->ScheduleDestruction();
|
||||
}
|
||||
}
|
||||
bool Open();
|
||||
void Close();
|
||||
private:
|
||||
/* NOTE: This has to be defined *after* KThread is defined. */
|
||||
/* Nintendo seems to handle this by defining Open/Close() in a cpp, but we'd like them to remain in headers. */
|
||||
|
||||
@@ -131,11 +131,15 @@ namespace ams::kern {
|
||||
/* Handle pseudo-handles. */
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||
return GetCurrentProcessPointer();
|
||||
auto * const cur_process = GetCurrentProcessPointer();
|
||||
AMS_ASSUME(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
} else if constexpr (std::derived_from<KThread, T>) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||
return GetCurrentThreadPointer();
|
||||
auto * const cur_thread = GetCurrentThreadPointer();
|
||||
AMS_ASSUME(cur_thread != nullptr);
|
||||
return cur_thread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +163,11 @@ namespace ams::kern {
|
||||
|
||||
ALWAYS_INLINE KScopedAutoObject<KAutoObject> GetObjectForIpc(ams::svc::Handle handle, KThread *cur_thread) const {
|
||||
/* Handle pseudo-handles. */
|
||||
AMS_ASSUME(cur_thread != nullptr);
|
||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||
return static_cast<KAutoObject *>(static_cast<void *>(cur_thread->GetOwnerProcess()));
|
||||
auto * const cur_process = static_cast<KAutoObject *>(static_cast<void *>(cur_thread->GetOwnerProcess()));
|
||||
AMS_ASSUME(cur_process != nullptr);
|
||||
return cur_process;
|
||||
}
|
||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||
return static_cast<KAutoObject *>(cur_thread);
|
||||
@@ -305,7 +312,7 @@ namespace ams::kern {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
||||
constexpr NOINLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Handles must not have reserved bits set. */
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace ams::kern {
|
||||
};
|
||||
|
||||
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
return static_cast<KMemoryPermission>((perm & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((perm & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None));
|
||||
return static_cast<KMemoryPermission>((util::ToUnderlying(perm) & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((util::ToUnderlying(perm) & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None));
|
||||
}
|
||||
|
||||
enum KMemoryAttribute : u8 {
|
||||
|
||||
@@ -175,7 +175,14 @@ namespace ams::kern {
|
||||
return std::make_tuple(total_size, kernel_size);
|
||||
}
|
||||
|
||||
static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start);
|
||||
static void InitializeLinearMemoryAddresses(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) {
|
||||
/* Set static differences. */
|
||||
s_linear_phys_to_virt_diff = GetInteger(linear_virtual_start) - GetInteger(aligned_linear_phys_start);
|
||||
s_linear_virt_to_phys_diff = GetInteger(aligned_linear_phys_start) - GetInteger(linear_virtual_start);
|
||||
}
|
||||
|
||||
static void InitializeLinearMemoryRegionTrees();
|
||||
|
||||
static size_t GetResourceRegionSizeForInit();
|
||||
|
||||
static NOINLINE auto GetKernelRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); }
|
||||
|
||||
@@ -66,11 +66,41 @@ namespace ams::kern {
|
||||
consteval operator KMemoryRegionType() const { return static_cast<KMemoryRegionType>(m_value); }
|
||||
consteval ValueType GetValue() const { return m_value; }
|
||||
|
||||
consteval const KMemoryRegionTypeValue &Finalize() { m_finalized = true; return *this; }
|
||||
consteval const KMemoryRegionTypeValue &SetSparseOnly() { m_sparse_only = true; return *this; }
|
||||
consteval const KMemoryRegionTypeValue &SetDenseOnly() { m_dense_only = true; return *this; }
|
||||
consteval const KMemoryRegionTypeValue Finalize() {
|
||||
AMS_ASSUME(!m_finalized);
|
||||
|
||||
consteval KMemoryRegionTypeValue &SetAttribute(KMemoryRegionAttr attr) { AMS_ASSUME(!m_finalized); m_value |= attr; return *this; }
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_finalized = true;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
consteval const KMemoryRegionTypeValue SetSparseOnly() {
|
||||
AMS_ASSUME(!m_finalized);
|
||||
AMS_ASSUME(!m_sparse_only);
|
||||
AMS_ASSUME(!m_dense_only);
|
||||
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_sparse_only = true;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
consteval const KMemoryRegionTypeValue SetDenseOnly() {
|
||||
AMS_ASSUME(!m_finalized);
|
||||
AMS_ASSUME(!m_sparse_only);
|
||||
AMS_ASSUME(!m_dense_only);
|
||||
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_dense_only = true;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
consteval KMemoryRegionTypeValue SetAttribute(KMemoryRegionAttr attr) {
|
||||
AMS_ASSUME(!m_finalized);
|
||||
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value |= attr;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
consteval KMemoryRegionTypeValue DeriveInitial(size_t i, size_t next = BITSIZEOF(ValueType)) const {
|
||||
AMS_ASSUME(!m_finalized);
|
||||
|
||||
@@ -194,8 +194,20 @@ namespace ams::kern {
|
||||
static bool s_scheduler_update_needed;
|
||||
static KSchedulerPriorityQueue s_priority_queue;
|
||||
static LockType s_scheduler_lock;
|
||||
public:
|
||||
static consteval bool ValidateAssemblyOffsets();
|
||||
};
|
||||
|
||||
consteval bool KScheduler::ValidateAssemblyOffsets() {
|
||||
static_assert(__builtin_offsetof(KScheduler, m_state.needs_scheduling) == KSCHEDULER_NEEDS_SCHEDULING);
|
||||
static_assert(__builtin_offsetof(KScheduler, m_state.interrupt_task_thread_runnable) == KSCHEDULER_INTERRUPT_TASK_THREAD_RUNNABLE);
|
||||
static_assert(__builtin_offsetof(KScheduler, m_state.highest_priority_thread) == KSCHEDULER_HIGHEST_PRIORITY_THREAD);
|
||||
static_assert(__builtin_offsetof(KScheduler, m_state.idle_thread_stack) == KSCHEDULER_IDLE_THREAD_STACK);
|
||||
|
||||
return true;
|
||||
}
|
||||
static_assert(KScheduler::ValidateAssemblyOffsets());
|
||||
|
||||
class KScopedSchedulerLock : KScopedLock<KScheduler::LockType> {
|
||||
public:
|
||||
explicit ALWAYS_INLINE KScopedSchedulerLock() : KScopedLock(KScheduler::s_scheduler_lock) { /* ... */ }
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace ams::kern {
|
||||
static_assert(sizeof(SyncObjectBuffer::m_sync_objects) == sizeof(SyncObjectBuffer::m_handles));
|
||||
|
||||
struct ConditionVariableComparator {
|
||||
struct LightCompareType {
|
||||
struct RedBlackKeyType {
|
||||
uintptr_t m_cv_key;
|
||||
s32 m_priority;
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace ams::kern {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> requires (std::same_as<T, KThread> || std::same_as<T, LightCompareType>)
|
||||
template<typename T> requires (std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
|
||||
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KThread &rhs) {
|
||||
const uintptr_t l_key = lhs.GetConditionVariableKey();
|
||||
const uintptr_t r_key = rhs.GetConditionVariableKey();
|
||||
@@ -165,8 +165,8 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(ams::util::HasLightCompareType<ConditionVariableComparator>);
|
||||
static_assert(std::same_as<ams::util::LightCompareType<ConditionVariableComparator, void>, ConditionVariableComparator::LightCompareType>);
|
||||
static_assert(ams::util::HasRedBlackKeyType<ConditionVariableComparator>);
|
||||
static_assert(std::same_as<ams::util::RedBlackKeyType<ConditionVariableComparator, void>, ConditionVariableComparator::RedBlackKeyType>);
|
||||
private:
|
||||
static inline std::atomic<u64> s_next_thread_id = 0;
|
||||
private:
|
||||
@@ -487,7 +487,7 @@ namespace ams::kern {
|
||||
constexpr void *GetThreadLocalRegionHeapAddress() const { return m_tls_heap_address; }
|
||||
|
||||
constexpr KSynchronizationObject **GetSynchronizationObjectBuffer() { return std::addressof(m_sync_object_buffer.m_sync_objects[0]); }
|
||||
constexpr ams::svc::Handle *GetHandleBuffer() { return std::addressof(m_sync_object_buffer.m_handles[sizeof(m_sync_object_buffer.m_sync_objects) / sizeof(ams::svc::Handle) - ams::svc::ArgumentHandleCountMax]); }
|
||||
constexpr ams::svc::Handle *GetHandleBuffer() { return std::addressof(m_sync_object_buffer.m_handles[sizeof(m_sync_object_buffer.m_sync_objects) / (sizeof(ams::svc::Handle)) - ams::svc::ArgumentHandleCountMax]); }
|
||||
|
||||
u16 GetUserDisableCount() const { return static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->disable_count; }
|
||||
void SetInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 1; }
|
||||
@@ -546,7 +546,7 @@ namespace ams::kern {
|
||||
|
||||
constexpr u32 GetSuspendFlags() const { return m_suspend_allowed_flags & m_suspend_request_flags; }
|
||||
constexpr bool IsSuspended() const { return this->GetSuspendFlags() != 0; }
|
||||
constexpr bool IsSuspendRequested(SuspendType type) const { return (m_suspend_request_flags & (1u << (ThreadState_SuspendShift + type))) != 0; }
|
||||
constexpr bool IsSuspendRequested(SuspendType type) const { return (m_suspend_request_flags & (1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type)))) != 0; }
|
||||
constexpr bool IsSuspendRequested() const { return m_suspend_request_flags != 0; }
|
||||
void RequestSuspend(SuspendType type);
|
||||
void Resume(SuspendType type);
|
||||
|
||||
@@ -105,4 +105,10 @@ namespace ams::kern {
|
||||
}
|
||||
};
|
||||
|
||||
/* Miscellaneous sanity checking. */
|
||||
static_assert(ams::svc::ThreadLocalRegionSize == THREAD_LOCAL_REGION_SIZE);
|
||||
static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, message_buffer) == THREAD_LOCAL_REGION_MESSAGE_BUFFER);
|
||||
static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, disable_count) == THREAD_LOCAL_REGION_DISABLE_COUNT);
|
||||
static_assert(__builtin_offsetof(ams::svc::ThreadLocalRegion, interrupt_flag) == THREAD_LOCAL_REGION_INTERRUPT_FLAG);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace ams::kern::svc {
|
||||
|
||||
static constexpr size_t NumSupervisorCalls = 0xC0;
|
||||
static constexpr size_t NumSupervisorCalls = AMS_KERN_NUM_SUPERVISOR_CALLS;
|
||||
|
||||
#define AMS_KERN_SVC_DECLARE_ENUM_ID(ID, RETURN_TYPE, NAME, ...) \
|
||||
SvcId_##NAME = ID,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere/kern_build_config.hpp>
|
||||
#include <mesosphere/kern_select_assembly_offsets.h>
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP)
|
||||
|
||||
@@ -32,28 +33,28 @@
|
||||
\
|
||||
/* Save x0/x1/sp to the context. */ \
|
||||
ldr x1, [sp, #(8 * 0)]; \
|
||||
str x1, [x0, #(8 * 0)]; \
|
||||
str x1, [x0, #(EXCEPTION_CONTEXT_X0)]; \
|
||||
ldr x1, [sp, #(8 * 1)]; \
|
||||
str x1, [x0, #(8 * 1)]; \
|
||||
str x1, [x0, #(EXCEPTION_CONTEXT_X1)]; \
|
||||
\
|
||||
/* Save all other registers to the context. */ \
|
||||
stp x2, x3, [x0, #(8 * 2)]; \
|
||||
stp x4, x5, [x0, #(8 * 4)]; \
|
||||
stp x6, x7, [x0, #(8 * 6)]; \
|
||||
stp x8, x9, [x0, #(8 * 8)]; \
|
||||
stp x10, x11, [x0, #(8 * 10)]; \
|
||||
stp x12, x13, [x0, #(8 * 12)]; \
|
||||
stp x14, x15, [x0, #(8 * 14)]; \
|
||||
stp x16, x17, [x0, #(8 * 16)]; \
|
||||
stp x18, x19, [x0, #(8 * 18)]; \
|
||||
stp x20, x21, [x0, #(8 * 20)]; \
|
||||
stp x22, x23, [x0, #(8 * 22)]; \
|
||||
stp x24, x25, [x0, #(8 * 24)]; \
|
||||
stp x26, x27, [x0, #(8 * 26)]; \
|
||||
stp x28, x29, [x0, #(8 * 28)]; \
|
||||
stp x2, x3, [x0, #(EXCEPTION_CONTEXT_X2_X3)]; \
|
||||
stp x4, x5, [x0, #(EXCEPTION_CONTEXT_X4_X5)]; \
|
||||
stp x6, x7, [x0, #(EXCEPTION_CONTEXT_X6_X7)]; \
|
||||
stp x8, x9, [x0, #(EXCEPTION_CONTEXT_X8_X9)]; \
|
||||
stp x10, x11, [x0, #(EXCEPTION_CONTEXT_X10_X11)]; \
|
||||
stp x12, x13, [x0, #(EXCEPTION_CONTEXT_X12_X13)]; \
|
||||
stp x14, x15, [x0, #(EXCEPTION_CONTEXT_X14_X15)]; \
|
||||
stp x16, x17, [x0, #(EXCEPTION_CONTEXT_X16_X17)]; \
|
||||
stp x18, x19, [x0, #(EXCEPTION_CONTEXT_X18_X19)]; \
|
||||
stp x20, x21, [x0, #(EXCEPTION_CONTEXT_X20_X21)]; \
|
||||
stp x22, x23, [x0, #(EXCEPTION_CONTEXT_X22_X23)]; \
|
||||
stp x24, x25, [x0, #(EXCEPTION_CONTEXT_X24_X25)]; \
|
||||
stp x26, x27, [x0, #(EXCEPTION_CONTEXT_X26_X27)]; \
|
||||
stp x28, x29, [x0, #(EXCEPTION_CONTEXT_X28_X29)]; \
|
||||
\
|
||||
add x1, sp, #16; \
|
||||
stp x30, x1, [x0, #(8 * 30)]; \
|
||||
stp x30, x1, [x0, #(EXCEPTION_CONTEXT_X30_SP)]; \
|
||||
\
|
||||
/* Restore x0/x1. */ \
|
||||
ldp x0, x1, [sp], #16;
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
.type _ZN3ams4kern3svc25CallReturnFromException64Ev, %function
|
||||
_ZN3ams4kern3svc25CallReturnFromException64Ev:
|
||||
/* Save registers the SVC entry handler didn't. */
|
||||
stp x12, x13, [sp, #(8 * 12)]
|
||||
stp x14, x15, [sp, #(8 * 14)]
|
||||
stp x16, x17, [sp, #(8 * 16)]
|
||||
str x19, [sp, #(8 * 19)]
|
||||
stp x20, x21, [sp, #(8 * 20)]
|
||||
stp x22, x23, [sp, #(8 * 22)]
|
||||
stp x24, x25, [sp, #(8 * 24)]
|
||||
stp x26, x26, [sp, #(8 * 26)]
|
||||
stp x28, x29, [sp, #(8 * 28)]
|
||||
stp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
stp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
stp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)]
|
||||
str x19, [sp, #(EXCEPTION_CONTEXT_X19)]
|
||||
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
stp x26, x26, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
/* Call ams::kern::arch::arm64::ReturnFromException(result). */
|
||||
bl _ZN3ams4kern4arch5arm6419ReturnFromExceptionENS_6ResultE
|
||||
@@ -63,7 +63,7 @@ _ZN3ams4kern3svc14RestoreContextEm:
|
||||
|
||||
0: /* We should handle DPC. */
|
||||
/* Check the dpc flags. */
|
||||
ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
ldrb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
cbz w8, 1f
|
||||
|
||||
/* We have DPC to do! */
|
||||
@@ -83,32 +83,32 @@ _ZN3ams4kern3svc14RestoreContextEm:
|
||||
|
||||
1: /* We're done with DPC, and should return from the svc. */
|
||||
/* Clear our in-SVC note. */
|
||||
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x30, x8, [sp, #(8 * 30)]
|
||||
ldp x9, x10, [sp, #(8 * 32)]
|
||||
ldr x11, [sp, #(8 * 34)]
|
||||
ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
|
||||
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
msr sp_el0, x8
|
||||
msr elr_el1, x9
|
||||
msr spsr_el1, x10
|
||||
msr tpidr_el0, x11
|
||||
ldp x0, x1, [sp, #(8 * 0)]
|
||||
ldp x2, x3, [sp, #(8 * 2)]
|
||||
ldp x4, x5, [sp, #(8 * 4)]
|
||||
ldp x6, x7, [sp, #(8 * 6)]
|
||||
ldp x8, x9, [sp, #(8 * 8)]
|
||||
ldp x10, x11, [sp, #(8 * 10)]
|
||||
ldp x12, x13, [sp, #(8 * 12)]
|
||||
ldp x14, x15, [sp, #(8 * 14)]
|
||||
ldp x16, x17, [sp, #(8 * 16)]
|
||||
ldp x18, x19, [sp, #(8 * 18)]
|
||||
ldp x20, x21, [sp, #(8 * 20)]
|
||||
ldp x22, x23, [sp, #(8 * 22)]
|
||||
ldp x24, x25, [sp, #(8 * 24)]
|
||||
ldp x26, x27, [sp, #(8 * 26)]
|
||||
ldp x28, x29, [sp, #(8 * 28)]
|
||||
ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
ldp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)]
|
||||
ldp x18, x19, [sp, #(EXCEPTION_CONTEXT_X18_X19)]
|
||||
ldp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
ldp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
ldp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
ldp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
ldp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
add sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
eret
|
||||
|
||||
@@ -22,45 +22,45 @@
|
||||
.type _ZN3ams4kern4arch5arm6412SvcHandler64Ev, %function
|
||||
_ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
/* Create a KExceptionContext for the exception. */
|
||||
sub sp, sp, #0x120
|
||||
sub sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
|
||||
/* Save registers needed for ReturnFromException */
|
||||
stp x9, x10, [sp, #(8 * 9)]
|
||||
str x11, [sp, #(8 * 11)]
|
||||
str x18, [sp, #(8 * 18)]
|
||||
stp x9, x10, [sp, #(EXCEPTION_CONTEXT_X9_X10)]
|
||||
str x11, [sp, #(EXCEPTION_CONTEXT_X11)]
|
||||
str x18, [sp, #(EXCEPTION_CONTEXT_X18)]
|
||||
|
||||
mrs x8, sp_el0
|
||||
mrs x9, elr_el1
|
||||
mrs x10, spsr_el1
|
||||
mrs x11, tpidr_el0
|
||||
ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)]
|
||||
ldr x18, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CUR_THREAD)]
|
||||
|
||||
/* Save callee-saved registers. */
|
||||
stp x19, x20, [sp, #(8 * 19)]
|
||||
stp x21, x22, [sp, #(8 * 21)]
|
||||
stp x23, x24, [sp, #(8 * 23)]
|
||||
stp x25, x26, [sp, #(8 * 25)]
|
||||
stp x27, x28, [sp, #(8 * 27)]
|
||||
stp x19, x20, [sp, #(EXCEPTION_CONTEXT_X19_X20)]
|
||||
stp x21, x22, [sp, #(EXCEPTION_CONTEXT_X21_X22)]
|
||||
stp x23, x24, [sp, #(EXCEPTION_CONTEXT_X23_X24)]
|
||||
stp x25, x26, [sp, #(EXCEPTION_CONTEXT_X25_X26)]
|
||||
stp x27, x28, [sp, #(EXCEPTION_CONTEXT_X27_X28)]
|
||||
|
||||
/* Save miscellaneous registers. */
|
||||
stp x0, x1, [sp, #(8 * 0)]
|
||||
stp x2, x3, [sp, #(8 * 2)]
|
||||
stp x4, x5, [sp, #(8 * 4)]
|
||||
stp x6, x7, [sp, #(8 * 6)]
|
||||
stp x29, x30, [sp, #(8 * 29)]
|
||||
stp x8, x9, [sp, #(8 * 31)]
|
||||
stp x10, x11, [sp, #(8 * 33)]
|
||||
stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
stp x29, x30, [sp, #(EXCEPTION_CONTEXT_X29_X30)]
|
||||
stp x8, x9, [sp, #(EXCEPTION_CONTEXT_SP_PC)]
|
||||
stp x10, x11, [sp, #(EXCEPTION_CONTEXT_PSR_TPIDR)]
|
||||
|
||||
/* Check if the SVC index is out of range. */
|
||||
mrs x8, esr_el1
|
||||
and x8, x8, #0xFF
|
||||
cmp x8, #0x80
|
||||
cmp x8, #(AMS_KERN_NUM_SUPERVISOR_CALLS)
|
||||
b.ge 3f
|
||||
|
||||
/* Check the specific SVC permission bit for allowal. */
|
||||
mov x9, sp
|
||||
add x9, x9, x8, lsr#3
|
||||
ldrb w9, [x9, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
|
||||
ldrb w9, [x9, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
|
||||
and x10, x8, #0x7
|
||||
lsr x10, x9, x10
|
||||
tst x10, #1
|
||||
@@ -68,11 +68,11 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
|
||||
/* Check if our disable count allows us to call SVCs. */
|
||||
mrs x10, tpidrro_el0
|
||||
ldrh w10, [x10, #0x100]
|
||||
ldrh w10, [x10, #(THREAD_LOCAL_REGION_DISABLE_COUNT)]
|
||||
cbz w10, 1f
|
||||
|
||||
/* It might not, so check the stack params to see if we must not allow the SVC. */
|
||||
ldrb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)]
|
||||
ldrb w10, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_PINNED)]
|
||||
cbz w10, 3f
|
||||
|
||||
1: /* We can call the SVC. */
|
||||
@@ -82,8 +82,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
|
||||
/* Note that we're calling the SVC. */
|
||||
mov w10, #1
|
||||
strb w10, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
|
||||
strb w10, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
|
||||
|
||||
/* If we should, trace the svc entry. */
|
||||
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
|
||||
@@ -110,7 +110,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
|
||||
2: /* We completed the SVC, and we should handle DPC. */
|
||||
/* Check the dpc flags. */
|
||||
ldrb w8, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
ldrb w8, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
cbz w8, 4f
|
||||
|
||||
/* We have DPC to do! */
|
||||
@@ -130,57 +130,57 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
|
||||
3: /* Invalid SVC. */
|
||||
/* Setup the context to call into HandleException. */
|
||||
stp x0, x1, [sp, #(8 * 0)]
|
||||
stp x2, x3, [sp, #(8 * 2)]
|
||||
stp x4, x5, [sp, #(8 * 4)]
|
||||
stp x6, x7, [sp, #(8 * 6)]
|
||||
stp xzr, xzr, [sp, #(8 * 8)]
|
||||
stp xzr, xzr, [sp, #(8 * 10)]
|
||||
stp xzr, xzr, [sp, #(8 * 12)]
|
||||
stp xzr, xzr, [sp, #(8 * 14)]
|
||||
stp xzr, xzr, [sp, #(8 * 16)]
|
||||
str x19, [sp, #(8 * 19)]
|
||||
stp x20, x21, [sp, #(8 * 20)]
|
||||
stp x22, x23, [sp, #(8 * 22)]
|
||||
stp x24, x25, [sp, #(8 * 24)]
|
||||
stp x26, x27, [sp, #(8 * 26)]
|
||||
stp x28, x29, [sp, #(8 * 28)]
|
||||
stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X16_X17)]
|
||||
str x19, [sp, #(EXCEPTION_CONTEXT_X19)]
|
||||
stp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
stp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
stp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
stp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
/* Call ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *) */
|
||||
mov x0, sp
|
||||
bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x30, x8, [sp, #(8 * 30)]
|
||||
ldp x9, x10, [sp, #(8 * 32)]
|
||||
ldr x11, [sp, #(8 * 34)]
|
||||
ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
|
||||
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
msr sp_el0, x8
|
||||
msr elr_el1, x9
|
||||
msr spsr_el1, x10
|
||||
msr tpidr_el0, x11
|
||||
ldp x0, x1, [sp, #(8 * 0)]
|
||||
ldp x2, x3, [sp, #(8 * 2)]
|
||||
ldp x4, x5, [sp, #(8 * 4)]
|
||||
ldp x6, x7, [sp, #(8 * 6)]
|
||||
ldp x8, x9, [sp, #(8 * 8)]
|
||||
ldp x10, x11, [sp, #(8 * 10)]
|
||||
ldp x12, x13, [sp, #(8 * 12)]
|
||||
ldp x14, x15, [sp, #(8 * 14)]
|
||||
ldp x16, x17, [sp, #(8 * 16)]
|
||||
ldp x18, x19, [sp, #(8 * 18)]
|
||||
ldp x20, x21, [sp, #(8 * 20)]
|
||||
ldp x22, x23, [sp, #(8 * 22)]
|
||||
ldp x24, x25, [sp, #(8 * 24)]
|
||||
ldp x26, x27, [sp, #(8 * 26)]
|
||||
ldp x28, x29, [sp, #(8 * 28)]
|
||||
ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
ldp x16, x17, [sp, #(EXCEPTION_CONTEXT_X16_X17)]
|
||||
ldp x18, x19, [sp, #(EXCEPTION_CONTEXT_X18_X19)]
|
||||
ldp x20, x21, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
ldp x22, x23, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
ldp x24, x25, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
ldp x26, x27, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
ldp x28, x29, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
add sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
eret
|
||||
|
||||
4: /* Return from SVC. */
|
||||
/* Clear our in-SVC note. */
|
||||
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
|
||||
/* If we should, trace the svc exit. */
|
||||
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
|
||||
@@ -199,10 +199,10 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
#endif
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x30, x8, [sp, #(8 * 30)]
|
||||
ldp x9, x10, [sp, #(8 * 32)]
|
||||
ldr x11, [sp, #(8 * 34)]
|
||||
ldr x18, [sp, #(8 * 18)]
|
||||
ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
|
||||
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
ldr x18, [sp, #(EXCEPTION_CONTEXT_X18)]
|
||||
msr sp_el0, x8
|
||||
msr elr_el1, x9
|
||||
msr spsr_el1, x10
|
||||
@@ -221,7 +221,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
|
||||
mov x17, xzr
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
add sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
eret
|
||||
|
||||
/* ams::kern::arch::arm64::SvcHandler32() */
|
||||
@@ -240,36 +240,36 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
mov w7, w7
|
||||
|
||||
/* Create a KExceptionContext for the exception. */
|
||||
sub sp, sp, #0x120
|
||||
sub sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
|
||||
/* Save system registers */
|
||||
mrs x17, elr_el1
|
||||
mrs x20, spsr_el1
|
||||
mrs x19, tpidr_el0
|
||||
ldr x18, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CUR_THREAD)]
|
||||
stp x17, x20, [sp, #(8 * 32)]
|
||||
str x19, [sp, #(8 * 34)]
|
||||
ldr x18, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CUR_THREAD)]
|
||||
stp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
str x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
|
||||
/* Save registers. */
|
||||
stp x0, x1, [sp, #(8 * 0)]
|
||||
stp x2, x3, [sp, #(8 * 2)]
|
||||
stp x4, x5, [sp, #(8 * 4)]
|
||||
stp x6, x7, [sp, #(8 * 6)]
|
||||
stp x8, x9, [sp, #(8 * 8)]
|
||||
stp x10, x11, [sp, #(8 * 10)]
|
||||
stp x12, x13, [sp, #(8 * 12)]
|
||||
stp x14, xzr, [sp, #(8 * 14)]
|
||||
stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
stp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
stp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
stp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
stp x14, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
|
||||
/* Check if the SVC index is out of range. */
|
||||
mrs x16, esr_el1
|
||||
and x16, x16, #0xFF
|
||||
cmp x16, #0x80
|
||||
cmp x16, #(AMS_KERN_NUM_SUPERVISOR_CALLS)
|
||||
b.ge 3f
|
||||
|
||||
/* Check the specific SVC permission bit for allowal. */
|
||||
mov x20, sp
|
||||
add x20, x20, x16, lsr#3
|
||||
ldrb w20, [x20, #(0x120 + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
|
||||
ldrb w20, [x20, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_SVC_PERMISSION)]
|
||||
and x17, x16, #0x7
|
||||
lsr x17, x20, x17
|
||||
tst x17, #1
|
||||
@@ -277,11 +277,11 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
|
||||
/* Check if our disable count allows us to call SVCs. */
|
||||
mrs x15, tpidrro_el0
|
||||
ldrh w15, [x15, #0x100]
|
||||
ldrh w15, [x15, #(THREAD_LOCAL_REGION_DISABLE_COUNT)]
|
||||
cbz w15, 1f
|
||||
|
||||
/* It might not, so check the stack params to see if we must not allow the SVC. */
|
||||
ldrb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_PINNED)]
|
||||
ldrb w15, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_PINNED)]
|
||||
cbz w15, 3f
|
||||
|
||||
1: /* We can call the SVC. */
|
||||
@@ -291,8 +291,8 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
|
||||
/* Note that we're calling the SVC. */
|
||||
mov w15, #1
|
||||
strb w15, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
|
||||
strb w15, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb w16, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_CURRENT_SVC_ID)]
|
||||
|
||||
/* If we should, trace the svc entry. */
|
||||
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
|
||||
@@ -319,7 +319,7 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
|
||||
2: /* We completed the SVC, and we should handle DPC. */
|
||||
/* Check the dpc flags. */
|
||||
ldrb w16, [sp, #(0x120 + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
ldrb w16, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_DPC_FLAGS)]
|
||||
cbz w16, 4f
|
||||
|
||||
/* We have DPC to do! */
|
||||
@@ -339,45 +339,45 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
|
||||
3: /* Invalid SVC. */
|
||||
/* Setup the context to call into HandleException. */
|
||||
stp x0, x1, [sp, #(8 * 0)]
|
||||
stp x2, x3, [sp, #(8 * 2)]
|
||||
stp x4, x5, [sp, #(8 * 4)]
|
||||
stp x6, x7, [sp, #(8 * 6)]
|
||||
stp xzr, xzr, [sp, #(8 * 16)]
|
||||
stp xzr, xzr, [sp, #(8 * 18)]
|
||||
stp xzr, xzr, [sp, #(8 * 20)]
|
||||
stp xzr, xzr, [sp, #(8 * 22)]
|
||||
stp xzr, xzr, [sp, #(8 * 24)]
|
||||
stp xzr, xzr, [sp, #(8 * 26)]
|
||||
stp xzr, xzr, [sp, #(8 * 28)]
|
||||
stp xzr, xzr, [sp, #(8 * 30)]
|
||||
stp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
stp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
stp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
stp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X16_X17)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X18_X19)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X20_X21)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X22_X23)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X24_X25)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X26_X27)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X28_X29)]
|
||||
stp xzr, xzr, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
|
||||
|
||||
/* Call ams::kern::arch::arm64::HandleException(ams::kern::arch::arm64::KExceptionContext *) */
|
||||
mov x0, sp
|
||||
bl _ZN3ams4kern4arch5arm6415HandleExceptionEPNS2_17KExceptionContextE
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x17, x20, [sp, #(8 * 32)]
|
||||
ldr x19, [sp, #(8 * 34)]
|
||||
ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
msr elr_el1, x17
|
||||
msr spsr_el1, x20
|
||||
msr tpidr_el0, x19
|
||||
ldp x0, x1, [sp, #(8 * 0)]
|
||||
ldp x2, x3, [sp, #(8 * 2)]
|
||||
ldp x4, x5, [sp, #(8 * 4)]
|
||||
ldp x6, x7, [sp, #(8 * 6)]
|
||||
ldp x8, x9, [sp, #(8 * 8)]
|
||||
ldp x10, x11, [sp, #(8 * 10)]
|
||||
ldp x12, x13, [sp, #(8 * 12)]
|
||||
ldp x14, x15, [sp, #(8 * 14)]
|
||||
ldp x0, x1, [sp, #(EXCEPTION_CONTEXT_X0_X1)]
|
||||
ldp x2, x3, [sp, #(EXCEPTION_CONTEXT_X2_X3)]
|
||||
ldp x4, x5, [sp, #(EXCEPTION_CONTEXT_X4_X5)]
|
||||
ldp x6, x7, [sp, #(EXCEPTION_CONTEXT_X6_X7)]
|
||||
ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
ldp x14, x15, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
add sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
eret
|
||||
|
||||
4: /* Return from SVC. */
|
||||
/* Clear our in-SVC note. */
|
||||
strb wzr, [sp, #(0x120 + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
strb wzr, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_IS_CALLING_SVC)]
|
||||
|
||||
/* If we should, trace the svc exit. */
|
||||
#if defined(MESOSPHERE_BUILD_FOR_TRACING)
|
||||
@@ -396,16 +396,16 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
|
||||
#endif
|
||||
|
||||
/* Restore registers. */
|
||||
ldp x8, x9, [sp, #(8 * 8)]
|
||||
ldp x10, x11, [sp, #(8 * 10)]
|
||||
ldp x12, x13, [sp, #(8 * 12)]
|
||||
ldp x14, xzr, [sp, #(8 * 14)]
|
||||
ldp x17, x20, [sp, #(8 * 32)]
|
||||
ldr x19, [sp, #(8 * 34)]
|
||||
ldp x8, x9, [sp, #(EXCEPTION_CONTEXT_X8_X9)]
|
||||
ldp x10, x11, [sp, #(EXCEPTION_CONTEXT_X10_X11)]
|
||||
ldp x12, x13, [sp, #(EXCEPTION_CONTEXT_X12_X13)]
|
||||
ldp x14, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
|
||||
ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
|
||||
ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
|
||||
msr elr_el1, x17
|
||||
msr spsr_el1, x20
|
||||
msr tpidr_el0, x19
|
||||
|
||||
/* Return. */
|
||||
add sp, sp, #0x120
|
||||
add sp, sp, #(EXCEPTION_CONTEXT_SIZE)
|
||||
eret
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
|
||||
#define cpuactlr_el1 s3_1_c15_c2_0
|
||||
#define cpuectlr_el1 s3_1_c15_c2_1
|
||||
|
||||
@@ -31,10 +31,12 @@ namespace ams::kern {
|
||||
constinit u64 g_initial_process_id_min = std::numeric_limits<u64>::max();
|
||||
constinit u64 g_initial_process_id_max = std::numeric_limits<u64>::min();
|
||||
|
||||
void LoadInitialProcessBinaryHeader() {
|
||||
void LoadInitialProcessBinaryHeader(KVirtualAddress virt_addr = Null<KVirtualAddress>) {
|
||||
if (g_initial_process_binary_header.magic != InitialProcessBinaryMagic) {
|
||||
/* Get the virtual address for the image. */
|
||||
const KVirtualAddress virt_addr = GetInitialProcessBinaryAddress();
|
||||
/* Get the virtual address, if it's not overridden. */
|
||||
if (virt_addr == Null<KVirtualAddress>) {
|
||||
virt_addr = GetInitialProcessBinaryAddress();
|
||||
}
|
||||
|
||||
/* Copy and validate the header. */
|
||||
g_initial_process_binary_header = *GetPointer<InitialProcessBinaryHeader>(virt_addr);
|
||||
@@ -54,12 +56,16 @@ namespace ams::kern {
|
||||
|
||||
/* Attach to the current KIP. */
|
||||
KInitialProcessReader reader;
|
||||
MESOSPHERE_ABORT_UNLESS(reader.Attach(current) != Null<KVirtualAddress>);
|
||||
KVirtualAddress data = reader.Attach(current);
|
||||
MESOSPHERE_ABORT_UNLESS(data != Null<KVirtualAddress>);
|
||||
|
||||
/* If the process uses secure memory, account for that. */
|
||||
if (reader.UsesSecureMemory()) {
|
||||
g_initial_process_secure_memory_size += reader.GetSize() + util::AlignUp(reader.GetStackSize(), PageSize);
|
||||
}
|
||||
|
||||
/* Advance to the next KIP. */
|
||||
current = data + reader.GetBinarySize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,6 +273,10 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KVirtualAddress GetInitialProcessBinaryAddress(KVirtualAddress pool_end) {
|
||||
return pool_end - InitialProcessBinarySizeMax;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u64 GetInitialProcessIdMin() {
|
||||
@@ -283,7 +293,7 @@ namespace ams::kern {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(pool_region != nullptr);
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(pool_region->GetEndAddress() != 0);
|
||||
MESOSPHERE_ABORT_UNLESS(pool_region->GetSize() >= InitialProcessBinarySizeMax);
|
||||
return pool_region->GetEndAddress() - InitialProcessBinarySizeMax;
|
||||
return GetInitialProcessBinaryAddress(pool_region->GetEndAddress());
|
||||
}
|
||||
|
||||
size_t GetInitialProcessesSecureMemorySize() {
|
||||
@@ -311,6 +321,10 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
void LoadInitialProcessBinaryHeaderDeprecated(KPhysicalAddress pool_end) {
|
||||
LoadInitialProcessBinaryHeader(GetInitialProcessBinaryAddress(KMemoryLayout::GetLinearVirtualAddress(pool_end)));
|
||||
}
|
||||
|
||||
void CreateAndRunInitialProcesses() {
|
||||
/* Allocate space for the processes. */
|
||||
InitialProcessInfo *infos = static_cast<InitialProcessInfo *>(__builtin_alloca(sizeof(InitialProcessInfo) * g_initial_process_binary_header.num_processes));
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto it = m_tree.nfind_light({ addr, -1 });
|
||||
auto it = m_tree.nfind_key({ addr, -1 });
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) {
|
||||
KThread *target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess());
|
||||
@@ -78,7 +78,7 @@ namespace ams::kern {
|
||||
R_UNLESS(UpdateIfEqual(std::addressof(user_value), addr, value, value + 1), svc::ResultInvalidCurrentMemory());
|
||||
R_UNLESS(user_value == value, svc::ResultInvalidState());
|
||||
|
||||
auto it = m_tree.nfind_light({ addr, -1 });
|
||||
auto it = m_tree.nfind_key({ addr, -1 });
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) {
|
||||
KThread *target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, ResultSuccess());
|
||||
@@ -100,7 +100,7 @@ namespace ams::kern {
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto it = m_tree.nfind_light({ addr, -1 });
|
||||
auto it = m_tree.nfind_key({ addr, -1 });
|
||||
/* Determine the updated value. */
|
||||
s32 new_value;
|
||||
if (GetTargetFirmware() >= TargetFirmware_7_0_0) {
|
||||
|
||||
@@ -22,19 +22,19 @@ namespace ams::kern {
|
||||
constexpr uintptr_t Invalid = std::numeric_limits<uintptr_t>::max();
|
||||
|
||||
constexpr KAddressSpaceInfo AddressSpaceInfos[] = {
|
||||
{ .bit_width = 32, .address = 2_MB, .size = 1_GB - 2_MB, .type = KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ .bit_width = 32, .address = 1_GB, .size = 4_GB - 1_GB, .type = KAddressSpaceInfo::Type_MapLarge, },
|
||||
{ .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Heap, },
|
||||
{ .bit_width = 32, .address = Invalid, .size = 1_GB, .type = KAddressSpaceInfo::Type_Alias, },
|
||||
{ .bit_width = 36, .address = 128_MB, .size = 2_GB - 128_MB, .type = KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ .bit_width = 36, .address = 2_GB, .size = 64_GB - 2_GB, .type = KAddressSpaceInfo::Type_MapLarge, },
|
||||
{ .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, },
|
||||
{ .bit_width = 36, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Alias, },
|
||||
{ .bit_width = 39, .address = 128_MB, .size = 512_GB - 128_MB, .type = KAddressSpaceInfo::Type_Map39Bit, },
|
||||
{ .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ .bit_width = 39, .address = Invalid, .size = 6_GB, .type = KAddressSpaceInfo::Type_Heap, },
|
||||
{ .bit_width = 39, .address = Invalid, .size = 64_GB, .type = KAddressSpaceInfo::Type_Alias, },
|
||||
{ .bit_width = 39, .address = Invalid, .size = 2_GB, .type = KAddressSpaceInfo::Type_Stack, },
|
||||
{ 32, 2_MB, 1_GB - 2_MB, KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ 32, 1_GB, 4_GB - 1_GB, KAddressSpaceInfo::Type_MapLarge, },
|
||||
{ 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Heap, },
|
||||
{ 32, Invalid, 1_GB, KAddressSpaceInfo::Type_Alias, },
|
||||
{ 36, 128_MB, 2_GB - 128_MB, KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ 36, 2_GB, 64_GB - 2_GB, KAddressSpaceInfo::Type_MapLarge, },
|
||||
{ 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, },
|
||||
{ 36, Invalid, 6_GB, KAddressSpaceInfo::Type_Alias, },
|
||||
{ 39, 128_MB, 512_GB - 128_MB, KAddressSpaceInfo::Type_Map39Bit, },
|
||||
{ 39, Invalid, 64_GB, KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ 39, Invalid, 6_GB, KAddressSpaceInfo::Type_Heap, },
|
||||
{ 39, Invalid, 64_GB, KAddressSpaceInfo::Type_Alias, },
|
||||
{ 39, Invalid, 2_GB, KAddressSpaceInfo::Type_Stack, },
|
||||
};
|
||||
|
||||
constexpr bool IsAllowedIndexForAddress(size_t index) {
|
||||
|
||||
@@ -22,4 +22,35 @@ namespace ams::kern {
|
||||
return obj;
|
||||
}
|
||||
|
||||
NOINLINE bool KAutoObject::Open() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Atomically increment the reference count, only if it's positive. */
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_relaxed);
|
||||
do {
|
||||
if (AMS_UNLIKELY(cur_ref_count == 0)) {
|
||||
MESOSPHERE_AUDIT(cur_ref_count != 0);
|
||||
return false;
|
||||
}
|
||||
MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void KAutoObject::Close() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Atomically decrement the reference count, not allowing it to become negative. */
|
||||
u32 cur_ref_count = m_ref_count.load(std::memory_order_relaxed);
|
||||
do {
|
||||
MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0);
|
||||
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed));
|
||||
|
||||
/* If ref count hits zero, schedule the object for destruction. */
|
||||
if (cur_ref_count - 1 == 0) {
|
||||
this->ScheduleDestruction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace ams::kern {
|
||||
{
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto it = m_tree.nfind_light({ cv_key, -1 });
|
||||
auto it = m_tree.nfind_key({ cv_key, -1 });
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) {
|
||||
KThread *target_thread = std::addressof(*it);
|
||||
|
||||
|
||||
@@ -308,8 +308,11 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* Send an exception event to represent our breaking the process. */
|
||||
static_assert(util::size(thread_ids) >= 4);
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[0], thread_ids[1], thread_ids[2], thread_ids[3]);
|
||||
/* TODO: How should this be handled in the case of more than 4 physical cores? */
|
||||
static_assert(util::size(thread_ids) <= 4);
|
||||
[&]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerBreak, thread_ids[Ix]...);
|
||||
}(std::make_index_sequence<util::size(thread_ids)>());
|
||||
|
||||
/* Signal. */
|
||||
this->NotifyAvailable();
|
||||
|
||||
@@ -25,13 +25,18 @@ namespace ams::kern {
|
||||
constexpr size_t CarveoutAlignment = 0x20000;
|
||||
constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment;
|
||||
|
||||
template<typename... T> requires (std::same_as<T, KMemoryRegionAttr> && ...)
|
||||
constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) {
|
||||
return util::FromUnderlying<KMemoryRegionType>(util::ToUnderlying(base) | (util::ToUnderlying<T>(attr) | ...));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() {
|
||||
#if defined(MESOSPHERE_DEBUG_LOG_USE_UART)
|
||||
switch (KSystemControl::Init::GetDebugLogUartPort()) {
|
||||
case 0: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
|
||||
case 1: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
|
||||
case 2: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
|
||||
case 3: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
|
||||
case 0: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap));
|
||||
case 1: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap));
|
||||
case 2: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap));
|
||||
case 3: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, GetMemoryRegionType(KMemoryRegionType_Uart, KMemoryRegionAttr_ShouldKernelMap));
|
||||
default: return false;
|
||||
}
|
||||
#elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER)
|
||||
@@ -43,11 +48,11 @@ namespace ams::kern {
|
||||
|
||||
ALWAYS_INLINE bool SetupPowerManagementControllerMemoryRegion() {
|
||||
/* For backwards compatibility, the PMC must remain mappable on < 2.0.0. */
|
||||
const auto rtc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : static_cast<KMemoryRegionAttr>(0);
|
||||
const auto pmc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : KMemoryRegionAttr_ShouldKernelMap;
|
||||
const KMemoryRegionAttr rtc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : static_cast<KMemoryRegionAttr>(0);
|
||||
const KMemoryRegionAttr pmc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : KMemoryRegionAttr_ShouldKernelMap;
|
||||
|
||||
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, KMemoryRegionType_None | rtc_restrict_attr) &&
|
||||
KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, KMemoryRegionType_PowerManagementController | pmc_restrict_attr);
|
||||
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, GetMemoryRegionType(KMemoryRegionType_None, rtc_restrict_attr)) &&
|
||||
KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, GetMemoryRegionType(KMemoryRegionType_PowerManagementController, pmc_restrict_attr));
|
||||
}
|
||||
|
||||
void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) {
|
||||
@@ -67,29 +72,29 @@ namespace ams::kern {
|
||||
/* TODO: Give these constexpr defines somewhere? */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion());
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(SetupPowerManagementControllerMemoryRegion());
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController, KMemoryRegionAttr_NoUserMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController0, KMemoryRegionAttr_NoUserMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, GetMemoryRegionType(KMemoryRegionType_MemoryController1, KMemoryRegionAttr_NoUserMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, GetMemoryRegionType(KMemoryRegionType_InterruptDistributor, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, GetMemoryRegionType(KMemoryRegionType_InterruptCpuInterface, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap)));
|
||||
|
||||
/* Map IRAM unconditionally, to support debug-logging-to-iram build config. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x40000000, 0x40000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsIram, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
|
||||
if (GetTargetFirmware() >= TargetFirmware_2_0_0) {
|
||||
/* Prevent mapping the bpmp exception vectors or the ipatch region. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, GetMemoryRegionType(KMemoryRegionType_None, KMemoryRegionAttr_NoUserMap)));
|
||||
} else {
|
||||
/* Map devices required for legacy lps driver. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_LegacyLpsExceptionVectors | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60007000, 0x1000, KMemoryRegionType_LegacyLpsFlowController | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60004000, 0x1000, KMemoryRegionType_LegacyLpsPrimaryICtlr | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60001000, 0x1000, KMemoryRegionType_LegacyLpsSemaphore | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70016000, 0x1000, KMemoryRegionType_LegacyLpsAtomics | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60006000, 0x1000, KMemoryRegionType_LegacyLpsClkRst | KMemoryRegionAttr_ShouldKernelMap));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsExceptionVectors, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60007000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsFlowController, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60004000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsPrimaryICtlr, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60001000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsSemaphore, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70016000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsAtomics, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60006000, 0x1000, GetMemoryRegionType(KMemoryRegionType_LegacyLpsClkRst, KMemoryRegionAttr_ShouldKernelMap)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +190,12 @@ namespace ams::kern {
|
||||
static_assert(KMemoryManager::Pool_Unsafe == KMemoryManager::Pool_Application);
|
||||
static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System);
|
||||
|
||||
/* NOTE: Beginning with 12.0.0 (and always, in mesosphere), the initial process binary is at the end of the pool region. */
|
||||
/* However, this is problematic for < 5.0.0, because we require the initial process binary to be parsed in order */
|
||||
/* to determine the pool sizes. Hence, we will force an initial binary load with the known pool end directly, so */
|
||||
/* that we retain compatibility with lower firmware versions. */
|
||||
LoadInitialProcessBinaryHeaderDeprecated(pool_end);
|
||||
|
||||
/* Get Secure pool size. */
|
||||
const size_t secure_pool_size = [] ALWAYS_INLINE_LAMBDA (auto target_firmware) -> size_t {
|
||||
constexpr size_t LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */
|
||||
|
||||
@@ -148,11 +148,7 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
void KMemoryLayout::InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) {
|
||||
/* Set static differences. */
|
||||
s_linear_phys_to_virt_diff = GetInteger(linear_virtual_start) - GetInteger(aligned_linear_phys_start);
|
||||
s_linear_virt_to_phys_diff = GetInteger(aligned_linear_phys_start) - GetInteger(linear_virtual_start);
|
||||
|
||||
void KMemoryLayout::InitializeLinearMemoryRegionTrees() {
|
||||
/* Initialize linear trees. */
|
||||
for (auto ®ion : GetPhysicalMemoryRegionTree()) {
|
||||
if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
|
||||
|
||||
@@ -231,6 +231,10 @@ namespace ams::kern {
|
||||
next_thread = m_idle_thread;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != m_core_id) {
|
||||
next_thread->SetCurrentCore(m_core_id);
|
||||
}
|
||||
|
||||
/* If we're not actually switching thread, there's nothing to do. */
|
||||
if (next_thread == cur_thread) {
|
||||
return;
|
||||
@@ -263,10 +267,6 @@ namespace ams::kern {
|
||||
|
||||
MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread);
|
||||
|
||||
if (next_thread->GetCurrentCore() != m_core_id) {
|
||||
next_thread->SetCurrentCore(m_core_id);
|
||||
}
|
||||
|
||||
/* Switch the current process, if we're switching processes. */
|
||||
if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
|
||||
KProcess::Switch(cur_process, next_process);
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace ams::kern {
|
||||
/* Disallow performing thread suspension. */
|
||||
{
|
||||
/* Update our allow flags. */
|
||||
m_suspend_allowed_flags &= ~(1 << (SuspendType_Thread + ThreadState_SuspendShift));
|
||||
m_suspend_allowed_flags &= ~(1 << (util::ToUnderlying(SuspendType_Thread) + util::ToUnderlying(ThreadState_SuspendShift)));
|
||||
|
||||
/* Update our state. */
|
||||
this->UpdateState();
|
||||
@@ -485,7 +485,7 @@ namespace ams::kern {
|
||||
/* Allow performing thread suspension (if termination hasn't been requested). */
|
||||
if (!this->IsTerminationRequested()) {
|
||||
/* Update our allow flags. */
|
||||
m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift));
|
||||
m_suspend_allowed_flags |= (1 << (util::ToUnderlying(SuspendType_Thread) + util::ToUnderlying(ThreadState_SuspendShift)));
|
||||
|
||||
/* Update our state. */
|
||||
this->UpdateState();
|
||||
@@ -760,7 +760,7 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock lk;
|
||||
|
||||
/* Note the request in our flags. */
|
||||
m_suspend_request_flags |= (1u << (ThreadState_SuspendShift + type));
|
||||
m_suspend_request_flags |= (1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type)));
|
||||
|
||||
/* Try to perform the suspend. */
|
||||
this->TrySuspend();
|
||||
@@ -772,7 +772,7 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Clear the request in our flags. */
|
||||
m_suspend_request_flags &= ~(1u << (ThreadState_SuspendShift + type));
|
||||
m_suspend_request_flags &= ~(1u << (util::ToUnderlying(ThreadState_SuspendShift) + util::ToUnderlying(type)));
|
||||
|
||||
/* Update our state. */
|
||||
this->UpdateState();
|
||||
|
||||
@@ -124,6 +124,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
ams_environment_weak.o: CXXFLAGS += -fno-lto
|
||||
pm_info_api_weak.o: CXXFLAGS += -fno-lto
|
||||
hos_stratosphere_api.o: CXXFLAGS += -fno-lto
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
|
||||
@@ -20,14 +20,7 @@ namespace ams::dd {
|
||||
|
||||
using ProcessHandle = ::Handle;
|
||||
|
||||
/* TODO gcc-11: using MemoryPermission = os::MemoryPermission; using enum os::MemoryPermission; */
|
||||
|
||||
enum MemoryPermission {
|
||||
MemoryPermission_None = 0,
|
||||
MemoryPermission_ReadOnly = (1u << 0),
|
||||
MemoryPermission_WriteOnly = (1u << 1),
|
||||
|
||||
MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly,
|
||||
};
|
||||
using MemoryPermission = os::MemoryPermission;
|
||||
using enum os::MemoryPermission;
|
||||
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetInitialLaunchSettingsCompletionTime, (const time::SteadyClockTimePoint &time_point), (time_point), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, ClearInitialLaunchSettingsCompletionTime, (), (), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, UpdatePowerOnTime, (), (), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0, hos::Version_12_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0, hos::Version_12_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, UpdateApplicationLaunchTime, (), (), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, ClearApplicationLaunchTime, (), (), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, SubmitAttachment, (ams::sf::Out<erpt::AttachmentId> out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_8_0_0) \
|
||||
|
||||
@@ -22,8 +22,9 @@ namespace ams::erpt::srv {
|
||||
|
||||
constexpr inline const char ReportOnSdStoragePath[] = "ersd";
|
||||
|
||||
constexpr inline const char ReportStoragePath[] = "save";
|
||||
constexpr inline const char JournalFileName[] = "save:/journal";
|
||||
constexpr inline const char ReportStoragePath[] = "save";
|
||||
constexpr inline const char JournalFileName[] = "save:/journal";
|
||||
constexpr inline const char ForcedShutdownContextFileName[] = "save:/forced-shutdown";
|
||||
|
||||
constexpr size_t ReportFileNameLength = 64;
|
||||
constexpr size_t AttachmentFileNameLength = 64;
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere/err/err_types.hpp>
|
||||
#include <stratosphere/err/err_error_context.hpp>
|
||||
#include <stratosphere/err/err_system_api.hpp>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/err/err_types.hpp>
|
||||
|
||||
namespace ams::err {
|
||||
|
||||
ErrorCode ConvertResultToErrorCode(const Result &result);
|
||||
void GetErrorCodeString(char *dst, size_t dst_size, ErrorCode error_code);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::err {
|
||||
|
||||
using ErrorCodeCategory = u32;
|
||||
using ErrorCodeNumber = u32;
|
||||
|
||||
struct ErrorCode {
|
||||
static constexpr auto StringLengthMax = 15;
|
||||
|
||||
ErrorCodeCategory category;
|
||||
ErrorCodeNumber number;
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsValid() const { return this->category > 0; }
|
||||
};
|
||||
|
||||
constexpr inline ErrorCode InvalidErrorCode = {
|
||||
.category = 0,
|
||||
.number = 0,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace ams::fs {
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
switch (op_id) {
|
||||
case OperationId::InvalidateCache:
|
||||
case OperationId::Invalidate:
|
||||
return ResultSuccess();
|
||||
case OperationId::QueryRange:
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
enum class OperationId : u64 {
|
||||
Clear = ::FsOperationId_Clear,
|
||||
ClearSignature = ::FsOperationId_ClearSignature,
|
||||
InvalidateCache = ::FsOperationId_InvalidateCache,
|
||||
QueryRange = ::FsOperationId_QueryRange,
|
||||
enum class OperationId : s64 {
|
||||
FillZero = 0,
|
||||
DestroySignature = 1,
|
||||
Invalidate = 2,
|
||||
QueryRange = 3,
|
||||
QueryUnpreparedRange = 4,
|
||||
QueryLazyLoadCompletionRate = 5,
|
||||
SetLazyLoadPriority = 6,
|
||||
|
||||
ReadLazyLoadFileForciblyForDebug = 10001,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace ams::fs {
|
||||
|
||||
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
switch (op_id) {
|
||||
case OperationId::InvalidateCache:
|
||||
case OperationId::Invalidate:
|
||||
case OperationId::QueryRange:
|
||||
return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
default:
|
||||
|
||||
@@ -20,23 +20,23 @@ namespace ams::fs::impl {
|
||||
|
||||
class Newable {
|
||||
public:
|
||||
static void *operator new(size_t size) {
|
||||
static ALWAYS_INLINE void *operator new(size_t size) noexcept {
|
||||
return ::ams::fs::impl::Allocate(size);
|
||||
}
|
||||
|
||||
static void *operator new(size_t size, Newable *placement) {
|
||||
static ALWAYS_INLINE void *operator new(size_t size, Newable *placement) noexcept {
|
||||
return placement;
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size) {
|
||||
static ALWAYS_INLINE void *operator new[](size_t size) noexcept {
|
||||
return ::ams::fs::impl::Allocate(size);
|
||||
}
|
||||
|
||||
static void operator delete(void *ptr, size_t size) {
|
||||
static ALWAYS_INLINE void operator delete(void *ptr, size_t size) noexcept {
|
||||
return ::ams::fs::impl::Deallocate(ptr, size);
|
||||
}
|
||||
|
||||
static void operator delete[](void *ptr, size_t size) {
|
||||
static ALWAYS_INLINE void operator delete[](void *ptr, size_t size) noexcept {
|
||||
return ::ams::fs::impl::Deallocate(ptr, size);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,11 +106,9 @@ namespace ams::fssystem {
|
||||
|
||||
using Newable::operator new;
|
||||
using Newable::operator delete;
|
||||
static void *operator new(size_t, void *p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
static void operator delete(void *, size_t, void*) { /* ... */ }
|
||||
static ALWAYS_INLINE void *operator new(size_t, void *p) noexcept { return p; }
|
||||
static ALWAYS_INLINE void operator delete(void *, size_t, void*) noexcept { /* ... */ }
|
||||
};
|
||||
|
||||
using AttrListTraits = util::IntrusiveListBaseTraits<AttrInfo>;
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace ams::hos {
|
||||
Version_11_0_0 = ::ams::TargetFirmware_11_0_0,
|
||||
Version_11_0_1 = ::ams::TargetFirmware_11_0_1,
|
||||
Version_12_0_0 = ::ams::TargetFirmware_12_0_0,
|
||||
Version_12_0_1 = ::ams::TargetFirmware_12_0_1,
|
||||
Version_12_0_2 = ::ams::TargetFirmware_12_0_2,
|
||||
Version_12_0_3 = ::ams::TargetFirmware_12_0_3,
|
||||
|
||||
Version_Current = ::ams::TargetFirmware_Current,
|
||||
|
||||
|
||||
@@ -60,174 +60,178 @@ namespace ams::i2c {
|
||||
}
|
||||
|
||||
enum I2cDevice : u32 {
|
||||
I2cDevice_ClassicController = 0,
|
||||
I2cDevice_Ftm3bd56 = 1,
|
||||
I2cDevice_Tmp451 = 2,
|
||||
I2cDevice_Nct72 = 3,
|
||||
I2cDevice_Alc5639 = 4,
|
||||
I2cDevice_Max77620Rtc = 5,
|
||||
I2cDevice_Max77620Pmic = 6,
|
||||
I2cDevice_Max77621Cpu = 7,
|
||||
I2cDevice_Max77621Gpu = 8,
|
||||
I2cDevice_Bq24193 = 9,
|
||||
I2cDevice_Max17050 = 10,
|
||||
I2cDevice_Bm92t30mwv = 11,
|
||||
I2cDevice_Ina226Vdd15v0Hb = 12,
|
||||
I2cDevice_ClassicController = 0,
|
||||
I2cDevice_Ftm3bd56 = 1,
|
||||
I2cDevice_Tmp451 = 2,
|
||||
I2cDevice_Nct72 = 3,
|
||||
I2cDevice_Alc5639 = 4,
|
||||
I2cDevice_Max77620Rtc = 5,
|
||||
I2cDevice_Max77620Pmic = 6,
|
||||
I2cDevice_Max77621Cpu = 7,
|
||||
I2cDevice_Max77621Gpu = 8,
|
||||
I2cDevice_Bq24193 = 9,
|
||||
I2cDevice_Max17050 = 10,
|
||||
I2cDevice_Bm92t30mwv = 11,
|
||||
I2cDevice_Ina226Vdd15v0Hb = 12,
|
||||
|
||||
I2cDevice_Ina226VsysCpuDs = 13,
|
||||
I2cDevice_Ina226VddCpuAp = 13,
|
||||
I2cDevice_Ina226VsysCpuDs = 13,
|
||||
I2cDevice_Ina226VddCpuAp = 13,
|
||||
|
||||
I2cDevice_Ina226VsysGpuDs = 14,
|
||||
I2cDevice_Ina226VddGpuAp = 14,
|
||||
I2cDevice_Ina226VsysGpuDs = 14,
|
||||
I2cDevice_Ina226VddGpuAp = 14,
|
||||
|
||||
I2cDevice_Ina226VsysDdrDs = 15,
|
||||
I2cDevice_Ina226VddDdr1V1Pmic = 15,
|
||||
I2cDevice_Ina226VsysDdrDs = 15,
|
||||
I2cDevice_Ina226VddDdr1V1Pmic = 15,
|
||||
|
||||
I2cDevice_Ina226VsysAp = 16,
|
||||
I2cDevice_Ina226VsysBlDs = 17,
|
||||
I2cDevice_Bh1730 = 18,
|
||||
I2cDevice_Ina226VsysAp = 16,
|
||||
I2cDevice_Ina226VsysBlDs = 17,
|
||||
I2cDevice_Bh1730 = 18,
|
||||
|
||||
I2cDevice_Ina226VsysCore = 19,
|
||||
I2cDevice_Ina226VddCoreAp = 19,
|
||||
I2cDevice_Ina226VsysCore = 19,
|
||||
I2cDevice_Ina226VddCoreAp = 19,
|
||||
|
||||
I2cDevice_Ina226Soc1V8 = 20,
|
||||
I2cDevice_Ina226VddSoc1V8 = 20,
|
||||
I2cDevice_Ina226Soc1V8 = 20,
|
||||
I2cDevice_Ina226VddSoc1V8 = 20,
|
||||
|
||||
I2cDevice_Ina226Lpddr1V8 = 21,
|
||||
I2cDevice_Ina226Vdd1V8 = 21,
|
||||
I2cDevice_Ina226Lpddr1V8 = 21,
|
||||
I2cDevice_Ina226Vdd1V8 = 21,
|
||||
|
||||
I2cDevice_Ina226Reg1V32 = 22,
|
||||
I2cDevice_Ina226Vdd3V3Sys = 23,
|
||||
I2cDevice_HdmiDdc = 24,
|
||||
I2cDevice_HdmiScdc = 25,
|
||||
I2cDevice_HdmiHdcp = 26,
|
||||
I2cDevice_Fan53528 = 27,
|
||||
I2cDevice_Max77812_3 = 28,
|
||||
I2cDevice_Max77812_2 = 29,
|
||||
I2cDevice_Ina226VddDdr0V6 = 30,
|
||||
I2cDevice_HoagNfcIc = 31, /* TODO */
|
||||
I2cDevice_Ina226Reg1V32 = 22,
|
||||
I2cDevice_Ina226Vdd3V3Sys = 23,
|
||||
I2cDevice_HdmiDdc = 24,
|
||||
I2cDevice_HdmiScdc = 25,
|
||||
I2cDevice_HdmiHdcp = 26,
|
||||
I2cDevice_Fan53528 = 27,
|
||||
I2cDevice_Max77812_3 = 28,
|
||||
I2cDevice_Max77812_2 = 29,
|
||||
I2cDevice_Ina226VddDdr0V6 = 30,
|
||||
I2cDevice_HoagNfcIc = 31, /* TODO */
|
||||
I2cDevice_PmicUnknownAula_4_18 = 32, /* TODO */
|
||||
};
|
||||
|
||||
/* TODO: Better place for this? */
|
||||
constexpr inline const DeviceCode DeviceCode_ClassicController = 0x350000C9;
|
||||
constexpr inline const DeviceCode DeviceCode_Ftm3bd56 = 0x35000033;
|
||||
constexpr inline const DeviceCode DeviceCode_Tmp451 = 0x3E000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Nct72 = 0x3E000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Alc5639 = 0x33000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77620Rtc = 0x3B000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77620Pmic = 0x3A000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77621Cpu = 0x3A000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77621Gpu = 0x3A000004;
|
||||
constexpr inline const DeviceCode DeviceCode_Bq24193 = 0x39000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max17050 = 0x39000033;
|
||||
constexpr inline const DeviceCode DeviceCode_Bm92t30mwv = 0x040000C9;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd15v0Hb = 0x3F000401;
|
||||
constexpr inline const DeviceCode DeviceCode_ClassicController = 0x350000C9;
|
||||
constexpr inline const DeviceCode DeviceCode_Ftm3bd56 = 0x35000033;
|
||||
constexpr inline const DeviceCode DeviceCode_Tmp451 = 0x3E000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Nct72 = 0x3E000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Alc5639 = 0x33000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77620Rtc = 0x3B000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77620Pmic = 0x3A000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77621Cpu = 0x3A000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77621Gpu = 0x3A000004;
|
||||
constexpr inline const DeviceCode DeviceCode_Bq24193 = 0x39000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Max17050 = 0x39000033;
|
||||
constexpr inline const DeviceCode DeviceCode_Bm92t30mwv = 0x040000C9;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd15v0Hb = 0x3F000401;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysCpuDs = 0x3F000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddCpuAp = 0x3F000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysCpuDs = 0x3F000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddCpuAp = 0x3F000001;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysGpuDs = 0x3F000002;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddGpuAp = 0x3F000002;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysGpuDs = 0x3F000002;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddGpuAp = 0x3F000002;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysDdrDs = 0x3F000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddDdr1V1Pmi = 0x3F000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysDdrDs = 0x3F000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddDdr1V1Pmi = 0x3F000003;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysAp = 0x3F000402;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysBlDs = 0x3F000403;
|
||||
constexpr inline const DeviceCode DeviceCode_Bh1730 = 0x35000047;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysAp = 0x3F000402;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysBlDs = 0x3F000403;
|
||||
constexpr inline const DeviceCode DeviceCode_Bh1730 = 0x35000047;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysCore = 0x3F000404;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddCoreAp = 0x3F000404;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VsysCore = 0x3F000404;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddCoreAp = 0x3F000404;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Soc1V8 = 0x3F000405;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddSoc1V8 = 0x3F000405;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Soc1V8 = 0x3F000405;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddSoc1V8 = 0x3F000405;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Lpddr1V8 = 0x3F000406;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd1V8 = 0x3F000406;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Lpddr1V8 = 0x3F000406;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd1V8 = 0x3F000406;
|
||||
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Reg1V32 = 0x3F000407;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd3V3Sys = 0x3F000408;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiDdc = 0x34000001;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiScdc = 0x34000002;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiHdcp = 0x34000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Fan53528 = 0x3A000005;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77812_3 = 0x3A000002;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77812_2 = 0x3A000006;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddDdr0V6 = 0x3F000409;
|
||||
constexpr inline const DeviceCode DeviceCode_HoagNfcIc = 0x36000001;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Reg1V32 = 0x3F000407;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226Vdd3V3Sys = 0x3F000408;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiDdc = 0x34000001;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiScdc = 0x34000002;
|
||||
constexpr inline const DeviceCode DeviceCode_HdmiHdcp = 0x34000003;
|
||||
constexpr inline const DeviceCode DeviceCode_Fan53528 = 0x3A000005;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77812_3 = 0x3A000002;
|
||||
constexpr inline const DeviceCode DeviceCode_Max77812_2 = 0x3A000006;
|
||||
constexpr inline const DeviceCode DeviceCode_Ina226VddDdr0V6 = 0x3F000409;
|
||||
constexpr inline const DeviceCode DeviceCode_HoagNfcIc = 0x36000001;
|
||||
constexpr inline const DeviceCode DeviceCode_PmicUnknownAula_4_18 = 0x3A000007;
|
||||
|
||||
constexpr inline DeviceCode ConvertToDeviceCode(I2cDevice dv) {
|
||||
switch (dv) {
|
||||
case I2cDevice_ClassicController: return DeviceCode_ClassicController;
|
||||
case I2cDevice_Ftm3bd56: return DeviceCode_Ftm3bd56;
|
||||
case I2cDevice_Tmp451: return DeviceCode_Tmp451;
|
||||
case I2cDevice_Nct72: return DeviceCode_Nct72;
|
||||
case I2cDevice_Alc5639: return DeviceCode_Alc5639;
|
||||
case I2cDevice_Max77620Rtc: return DeviceCode_Max77620Rtc;
|
||||
case I2cDevice_Max77620Pmic: return DeviceCode_Max77620Pmic;
|
||||
case I2cDevice_Max77621Cpu: return DeviceCode_Max77621Cpu;
|
||||
case I2cDevice_Max77621Gpu: return DeviceCode_Max77621Gpu;
|
||||
case I2cDevice_Bq24193: return DeviceCode_Bq24193;
|
||||
case I2cDevice_Max17050: return DeviceCode_Max17050;
|
||||
case I2cDevice_Bm92t30mwv: return DeviceCode_Bm92t30mwv;
|
||||
case I2cDevice_Ina226Vdd15v0Hb: return DeviceCode_Ina226Vdd15v0Hb;
|
||||
case I2cDevice_Ina226VsysCpuDs: return DeviceCode_Ina226VsysCpuDs;
|
||||
case I2cDevice_Ina226VsysGpuDs: return DeviceCode_Ina226VsysGpuDs;
|
||||
case I2cDevice_Ina226VsysDdrDs: return DeviceCode_Ina226VsysDdrDs;
|
||||
case I2cDevice_Ina226VsysAp: return DeviceCode_Ina226VsysAp;
|
||||
case I2cDevice_Ina226VsysBlDs: return DeviceCode_Ina226VsysBlDs;
|
||||
case I2cDevice_Bh1730: return DeviceCode_Bh1730;
|
||||
case I2cDevice_Ina226VsysCore: return DeviceCode_Ina226VsysCore;
|
||||
case I2cDevice_Ina226Soc1V8: return DeviceCode_Ina226Soc1V8;
|
||||
case I2cDevice_Ina226Lpddr1V8: return DeviceCode_Ina226Lpddr1V8;
|
||||
case I2cDevice_Ina226Reg1V32: return DeviceCode_Ina226Reg1V32;
|
||||
case I2cDevice_Ina226Vdd3V3Sys: return DeviceCode_Ina226Vdd3V3Sys;
|
||||
case I2cDevice_HdmiDdc: return DeviceCode_HdmiDdc;
|
||||
case I2cDevice_HdmiScdc: return DeviceCode_HdmiScdc;
|
||||
case I2cDevice_HdmiHdcp: return DeviceCode_HdmiHdcp;
|
||||
case I2cDevice_Fan53528: return DeviceCode_Fan53528;
|
||||
case I2cDevice_Max77812_3: return DeviceCode_Max77812_3;
|
||||
case I2cDevice_Max77812_2: return DeviceCode_Max77812_2;
|
||||
case I2cDevice_Ina226VddDdr0V6: return DeviceCode_Ina226VddDdr0V6;
|
||||
case I2cDevice_HoagNfcIc: return DeviceCode_HoagNfcIc;
|
||||
case I2cDevice_ClassicController: return DeviceCode_ClassicController;
|
||||
case I2cDevice_Ftm3bd56: return DeviceCode_Ftm3bd56;
|
||||
case I2cDevice_Tmp451: return DeviceCode_Tmp451;
|
||||
case I2cDevice_Nct72: return DeviceCode_Nct72;
|
||||
case I2cDevice_Alc5639: return DeviceCode_Alc5639;
|
||||
case I2cDevice_Max77620Rtc: return DeviceCode_Max77620Rtc;
|
||||
case I2cDevice_Max77620Pmic: return DeviceCode_Max77620Pmic;
|
||||
case I2cDevice_Max77621Cpu: return DeviceCode_Max77621Cpu;
|
||||
case I2cDevice_Max77621Gpu: return DeviceCode_Max77621Gpu;
|
||||
case I2cDevice_Bq24193: return DeviceCode_Bq24193;
|
||||
case I2cDevice_Max17050: return DeviceCode_Max17050;
|
||||
case I2cDevice_Bm92t30mwv: return DeviceCode_Bm92t30mwv;
|
||||
case I2cDevice_Ina226Vdd15v0Hb: return DeviceCode_Ina226Vdd15v0Hb;
|
||||
case I2cDevice_Ina226VsysCpuDs: return DeviceCode_Ina226VsysCpuDs;
|
||||
case I2cDevice_Ina226VsysGpuDs: return DeviceCode_Ina226VsysGpuDs;
|
||||
case I2cDevice_Ina226VsysDdrDs: return DeviceCode_Ina226VsysDdrDs;
|
||||
case I2cDevice_Ina226VsysAp: return DeviceCode_Ina226VsysAp;
|
||||
case I2cDevice_Ina226VsysBlDs: return DeviceCode_Ina226VsysBlDs;
|
||||
case I2cDevice_Bh1730: return DeviceCode_Bh1730;
|
||||
case I2cDevice_Ina226VsysCore: return DeviceCode_Ina226VsysCore;
|
||||
case I2cDevice_Ina226Soc1V8: return DeviceCode_Ina226Soc1V8;
|
||||
case I2cDevice_Ina226Lpddr1V8: return DeviceCode_Ina226Lpddr1V8;
|
||||
case I2cDevice_Ina226Reg1V32: return DeviceCode_Ina226Reg1V32;
|
||||
case I2cDevice_Ina226Vdd3V3Sys: return DeviceCode_Ina226Vdd3V3Sys;
|
||||
case I2cDevice_HdmiDdc: return DeviceCode_HdmiDdc;
|
||||
case I2cDevice_HdmiScdc: return DeviceCode_HdmiScdc;
|
||||
case I2cDevice_HdmiHdcp: return DeviceCode_HdmiHdcp;
|
||||
case I2cDevice_Fan53528: return DeviceCode_Fan53528;
|
||||
case I2cDevice_Max77812_3: return DeviceCode_Max77812_3;
|
||||
case I2cDevice_Max77812_2: return DeviceCode_Max77812_2;
|
||||
case I2cDevice_Ina226VddDdr0V6: return DeviceCode_Ina226VddDdr0V6;
|
||||
case I2cDevice_HoagNfcIc: return DeviceCode_HoagNfcIc;
|
||||
case I2cDevice_PmicUnknownAula_4_18: return DeviceCode_PmicUnknownAula_4_18;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline I2cDevice ConvertToI2cDevice(DeviceCode dc) {
|
||||
switch (dc.GetInternalValue()) {
|
||||
case DeviceCode_ClassicController.GetInternalValue(): return I2cDevice_ClassicController;
|
||||
case DeviceCode_Ftm3bd56 .GetInternalValue(): return I2cDevice_Ftm3bd56;
|
||||
case DeviceCode_Tmp451 .GetInternalValue(): return I2cDevice_Tmp451;
|
||||
/* case DeviceCode_Nct72 .GetInternalValue(): return I2cDevice_Nct72; */
|
||||
case DeviceCode_Alc5639 .GetInternalValue(): return I2cDevice_Alc5639;
|
||||
case DeviceCode_Max77620Rtc .GetInternalValue(): return I2cDevice_Max77620Rtc;
|
||||
case DeviceCode_Max77620Pmic .GetInternalValue(): return I2cDevice_Max77620Pmic;
|
||||
case DeviceCode_Max77621Cpu .GetInternalValue(): return I2cDevice_Max77621Cpu;
|
||||
case DeviceCode_Max77621Gpu .GetInternalValue(): return I2cDevice_Max77621Gpu;
|
||||
case DeviceCode_Bq24193 .GetInternalValue(): return I2cDevice_Bq24193;
|
||||
case DeviceCode_Max17050 .GetInternalValue(): return I2cDevice_Max17050;
|
||||
case DeviceCode_Bm92t30mwv .GetInternalValue(): return I2cDevice_Bm92t30mwv;
|
||||
case DeviceCode_Ina226Vdd15v0Hb .GetInternalValue(): return I2cDevice_Ina226Vdd15v0Hb;
|
||||
case DeviceCode_Ina226VsysCpuDs .GetInternalValue(): return I2cDevice_Ina226VsysCpuDs;
|
||||
case DeviceCode_Ina226VsysGpuDs .GetInternalValue(): return I2cDevice_Ina226VsysGpuDs;
|
||||
case DeviceCode_Ina226VsysDdrDs .GetInternalValue(): return I2cDevice_Ina226VsysDdrDs;
|
||||
case DeviceCode_Ina226VsysAp .GetInternalValue(): return I2cDevice_Ina226VsysAp;
|
||||
case DeviceCode_Ina226VsysBlDs .GetInternalValue(): return I2cDevice_Ina226VsysBlDs;
|
||||
case DeviceCode_Bh1730 .GetInternalValue(): return I2cDevice_Bh1730;
|
||||
case DeviceCode_Ina226VsysCore .GetInternalValue(): return I2cDevice_Ina226VsysCore;
|
||||
case DeviceCode_Ina226Soc1V8 .GetInternalValue(): return I2cDevice_Ina226Soc1V8;
|
||||
case DeviceCode_Ina226Lpddr1V8 .GetInternalValue(): return I2cDevice_Ina226Lpddr1V8;
|
||||
case DeviceCode_Ina226Reg1V32 .GetInternalValue(): return I2cDevice_Ina226Reg1V32;
|
||||
case DeviceCode_Ina226Vdd3V3Sys .GetInternalValue(): return I2cDevice_Ina226Vdd3V3Sys;
|
||||
case DeviceCode_HdmiDdc .GetInternalValue(): return I2cDevice_HdmiDdc;
|
||||
case DeviceCode_HdmiScdc .GetInternalValue(): return I2cDevice_HdmiScdc;
|
||||
case DeviceCode_HdmiHdcp .GetInternalValue(): return I2cDevice_HdmiHdcp;
|
||||
case DeviceCode_Fan53528 .GetInternalValue(): return I2cDevice_Fan53528;
|
||||
case DeviceCode_Max77812_3 .GetInternalValue(): return I2cDevice_Max77812_3;
|
||||
case DeviceCode_Max77812_2 .GetInternalValue(): return I2cDevice_Max77812_2;
|
||||
case DeviceCode_Ina226VddDdr0V6 .GetInternalValue(): return I2cDevice_Ina226VddDdr0V6;
|
||||
case DeviceCode_HoagNfcIc .GetInternalValue(): return I2cDevice_HoagNfcIc;
|
||||
case DeviceCode_ClassicController .GetInternalValue(): return I2cDevice_ClassicController;
|
||||
case DeviceCode_Ftm3bd56 .GetInternalValue(): return I2cDevice_Ftm3bd56;
|
||||
case DeviceCode_Tmp451 .GetInternalValue(): return I2cDevice_Tmp451;
|
||||
/* case DeviceCode_Nct72 .GetInternalValue(): return I2cDevice_Nct72; */
|
||||
case DeviceCode_Alc5639 .GetInternalValue(): return I2cDevice_Alc5639;
|
||||
case DeviceCode_Max77620Rtc .GetInternalValue(): return I2cDevice_Max77620Rtc;
|
||||
case DeviceCode_Max77620Pmic .GetInternalValue(): return I2cDevice_Max77620Pmic;
|
||||
case DeviceCode_Max77621Cpu .GetInternalValue(): return I2cDevice_Max77621Cpu;
|
||||
case DeviceCode_Max77621Gpu .GetInternalValue(): return I2cDevice_Max77621Gpu;
|
||||
case DeviceCode_Bq24193 .GetInternalValue(): return I2cDevice_Bq24193;
|
||||
case DeviceCode_Max17050 .GetInternalValue(): return I2cDevice_Max17050;
|
||||
case DeviceCode_Bm92t30mwv .GetInternalValue(): return I2cDevice_Bm92t30mwv;
|
||||
case DeviceCode_Ina226Vdd15v0Hb .GetInternalValue(): return I2cDevice_Ina226Vdd15v0Hb;
|
||||
case DeviceCode_Ina226VsysCpuDs .GetInternalValue(): return I2cDevice_Ina226VsysCpuDs;
|
||||
case DeviceCode_Ina226VsysGpuDs .GetInternalValue(): return I2cDevice_Ina226VsysGpuDs;
|
||||
case DeviceCode_Ina226VsysDdrDs .GetInternalValue(): return I2cDevice_Ina226VsysDdrDs;
|
||||
case DeviceCode_Ina226VsysAp .GetInternalValue(): return I2cDevice_Ina226VsysAp;
|
||||
case DeviceCode_Ina226VsysBlDs .GetInternalValue(): return I2cDevice_Ina226VsysBlDs;
|
||||
case DeviceCode_Bh1730 .GetInternalValue(): return I2cDevice_Bh1730;
|
||||
case DeviceCode_Ina226VsysCore .GetInternalValue(): return I2cDevice_Ina226VsysCore;
|
||||
case DeviceCode_Ina226Soc1V8 .GetInternalValue(): return I2cDevice_Ina226Soc1V8;
|
||||
case DeviceCode_Ina226Lpddr1V8 .GetInternalValue(): return I2cDevice_Ina226Lpddr1V8;
|
||||
case DeviceCode_Ina226Reg1V32 .GetInternalValue(): return I2cDevice_Ina226Reg1V32;
|
||||
case DeviceCode_Ina226Vdd3V3Sys .GetInternalValue(): return I2cDevice_Ina226Vdd3V3Sys;
|
||||
case DeviceCode_HdmiDdc .GetInternalValue(): return I2cDevice_HdmiDdc;
|
||||
case DeviceCode_HdmiScdc .GetInternalValue(): return I2cDevice_HdmiScdc;
|
||||
case DeviceCode_HdmiHdcp .GetInternalValue(): return I2cDevice_HdmiHdcp;
|
||||
case DeviceCode_Fan53528 .GetInternalValue(): return I2cDevice_Fan53528;
|
||||
case DeviceCode_Max77812_3 .GetInternalValue(): return I2cDevice_Max77812_3;
|
||||
case DeviceCode_Max77812_2 .GetInternalValue(): return I2cDevice_Max77812_2;
|
||||
case DeviceCode_Ina226VddDdr0V6 .GetInternalValue(): return I2cDevice_Ina226VddDdr0V6;
|
||||
case DeviceCode_HoagNfcIc .GetInternalValue(): return I2cDevice_HoagNfcIc;
|
||||
case DeviceCode_PmicUnknownAula_4_18.GetInternalValue(): return I2cDevice_PmicUnknownAula_4_18;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ams::kvdb {
|
||||
|
||||
/* Getters. */
|
||||
size_t GetLength() const {
|
||||
return strnlen(this->buffer, N);
|
||||
return util::Strnlen(this->buffer, N);
|
||||
}
|
||||
|
||||
const char *Get() const {
|
||||
@@ -72,7 +72,7 @@ namespace ams::kvdb {
|
||||
/* Setters. */
|
||||
void Set(const char *s) {
|
||||
/* Ensure string can fit in our buffer. */
|
||||
CheckLength(strnlen(s, N));
|
||||
CheckLength(util::Strnlen(s, N));
|
||||
std::strncpy(this->buffer, s, N);
|
||||
this->buffer[N - 1] = 0;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ namespace ams::kvdb {
|
||||
/* Append to existing. */
|
||||
void Append(const char *s) {
|
||||
const size_t length = GetLength();
|
||||
CheckLength(length + strnlen(s, N));
|
||||
CheckLength(length + util::Strnlen(s, N));
|
||||
std::strncat(this->buffer, s, N - length - 1);
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace ams::kvdb {
|
||||
}
|
||||
|
||||
bool EndsWith(const char *s) const {
|
||||
const size_t suffix_length = strnlen(s, N);
|
||||
const size_t suffix_length = util::Strnlen(s, N);
|
||||
const size_t length = GetLength();
|
||||
return suffix_length <= length && EndsWith(s, length - suffix_length);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,10 @@ namespace ams::sf::cmif {
|
||||
Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data);
|
||||
|
||||
constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const {
|
||||
return this->cmd_id == cmd_id && this->hosver_low <= hosver && hosver <= this->hosver_high;
|
||||
const bool min_valid = this->hosver_low == hos::Version_Min;
|
||||
const bool max_valid = this->hosver_high == hos::Version_Max;
|
||||
|
||||
return this->cmd_id == cmd_id && (min_valid || this->hosver_low <= hosver) && (max_valid || hosver <= this->hosver_high);
|
||||
}
|
||||
|
||||
constexpr inline decltype(handler) GetHandler() const {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <stratosphere/sm/sm_types.hpp>
|
||||
#include <stratosphere/sm/sm_api.hpp>
|
||||
#include <stratosphere/sm/sm_mitm_api.hpp>
|
||||
#include <stratosphere/sm/sm_scoped_holder.hpp>
|
||||
|
||||
#include <stratosphere/sm/sm_manager_api.hpp>
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
namespace ams::sm {
|
||||
|
||||
/* Initialization. */
|
||||
Result Initialize();
|
||||
Result Finalize();
|
||||
|
||||
/* Ordinary SM API. */
|
||||
Result GetService(Service *out, ServiceName name);
|
||||
Result RegisterService(Handle *out, ServiceName name, size_t max_sessions, bool is_light);
|
||||
@@ -29,17 +33,4 @@ namespace ams::sm {
|
||||
Result HasService(bool *out, ServiceName name);
|
||||
Result WaitService(ServiceName name);
|
||||
|
||||
/* Scoped session access. */
|
||||
namespace impl {
|
||||
|
||||
void DoWithSessionImpl(void (*Invoker)(void *), void *Function);
|
||||
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
NX_CONSTEXPR void DoWithSession(F f) {
|
||||
auto invoker = +[](void *func) { (*(F *)func)(); };
|
||||
impl::DoWithSessionImpl(invoker, &f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 "sm_api.hpp"
|
||||
|
||||
namespace ams::sm {
|
||||
|
||||
/* Utility, for scoped access to libnx services. */
|
||||
template<auto Initializer(), void Finalizer()>
|
||||
class ScopedServiceHolder {
|
||||
NON_COPYABLE(ScopedServiceHolder);
|
||||
private:
|
||||
Result result;
|
||||
bool has_initialized;
|
||||
public:
|
||||
ScopedServiceHolder(bool initialize = true) : result(ResultSuccess()), has_initialized(false) {
|
||||
if (initialize) {
|
||||
this->Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedServiceHolder() {
|
||||
if (this->has_initialized) {
|
||||
this->Finalize();
|
||||
}
|
||||
}
|
||||
|
||||
ScopedServiceHolder(ScopedServiceHolder&& rhs) {
|
||||
this->result = rhs.result;
|
||||
this->has_initialized = rhs.has_initialized;
|
||||
rhs.result = ResultSuccess();
|
||||
rhs.has_initialized = false;
|
||||
}
|
||||
|
||||
ScopedServiceHolder &operator=(ScopedServiceHolder&& rhs) {
|
||||
rhs.Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(ScopedServiceHolder &rhs) {
|
||||
std::swap(this->result, rhs.result);
|
||||
std::swap(this->has_initialized, rhs.has_initialized);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return this->has_initialized;
|
||||
}
|
||||
|
||||
Result Initialize() {
|
||||
AMS_ABORT_UNLESS(!this->has_initialized);
|
||||
|
||||
sm::DoWithSession([&]() {
|
||||
if constexpr (std::is_same<decltype(Initializer()), void>::value) {
|
||||
Initializer();
|
||||
this->result = ResultSuccess();
|
||||
} else {
|
||||
this->result = Initializer();
|
||||
}
|
||||
});
|
||||
|
||||
this->has_initialized = R_SUCCEEDED(this->result);
|
||||
return this->result;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
AMS_ABORT_UNLESS(this->has_initialized);
|
||||
Finalizer();
|
||||
this->has_initialized = false;
|
||||
}
|
||||
|
||||
Result GetResult() const {
|
||||
return this->result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -79,8 +79,9 @@ namespace ams::tipc::impl {
|
||||
|
||||
#define AMS_TIPC_IMPL_PROCESS_METHOD_REQUEST_BY_ID(CLASSNAME, CMD_ID, RETURN, NAME, ARGS, ARGNAMES, VERSION_MIN, VERSION_MAX) \
|
||||
if constexpr (constexpr u16 TipcCommandId = CMD_ID + 0x10; CommandId == TipcCommandId) { \
|
||||
constexpr bool AlwaysValid = VERSION_MIN == hos::Version_Min && VERSION_MAX == hos::Version_Max; \
|
||||
if (AlwaysValid || (VERSION_MIN <= fw_ver && fw_ver <= VERSION_MAX)) { \
|
||||
constexpr bool MinValid = VERSION_MIN == hos::Version_Min; \
|
||||
constexpr bool MaxValid = VERSION_MAX == hos::Version_Max; \
|
||||
if ((MinValid || VERSION_MIN <= fw_ver) && (MaxValid || fw_ver <= VERSION_MAX)) { \
|
||||
return ::ams::tipc::impl::InvokeServiceCommandImpl<TipcCommandId, &ImplType::NAME, ImplType>(impl, message_buffer); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ams::tipc {
|
||||
constexpr inline u16 MethodId_CloseSession = 0xF;
|
||||
|
||||
class ObjectManagerBase {
|
||||
private:
|
||||
protected:
|
||||
struct Entry {
|
||||
util::TypedStorage<WaitableObject> object;
|
||||
os::WaitableHolderType waitable_holder;
|
||||
|
||||
@@ -42,37 +42,44 @@ namespace ams::tipc {
|
||||
template<typename DeferralManagerType, size_t ThreadStackSize, typename... PortInfos>
|
||||
class ServerManagerImpl {
|
||||
private:
|
||||
static_assert(util::IsAligned(ThreadStackSize, os::ThreadStackAlignment));
|
||||
|
||||
static constexpr inline bool IsDeferralSupported = !std::same_as<DeferralManagerType, DummyDeferralManager>;
|
||||
using ResumeKey = typename DeferralManagerType::Key;
|
||||
|
||||
static ALWAYS_INLINE uintptr_t ConvertKeyToMessage(ResumeKey key) {
|
||||
static_assert(sizeof(key) <= sizeof(uintptr_t));
|
||||
static_assert(std::is_trivial<ResumeKey>::value);
|
||||
|
||||
/* TODO: std::bit_cast */
|
||||
uintptr_t converted = 0;
|
||||
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
|
||||
return converted;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE ResumeKey ConvertMessageToKey(uintptr_t message) {
|
||||
static_assert(sizeof(ResumeKey) <= sizeof(uintptr_t));
|
||||
static_assert(std::is_trivial<ResumeKey>::value);
|
||||
|
||||
/* TODO: std::bit_cast */
|
||||
ResumeKey converted = {};
|
||||
std::memcpy(std::addressof(converted), std::addressof(message), sizeof(converted));
|
||||
return converted;
|
||||
}
|
||||
|
||||
static constexpr inline size_t NumPorts = sizeof...(PortInfos);
|
||||
static constexpr inline size_t MaxSessions = (PortInfos::MaxSessions + ...);
|
||||
|
||||
/* Verify that it's possible to service this many sessions, with our port manager count. */
|
||||
static_assert(MaxSessions <= NumPorts * svc::ArgumentHandleCountMax);
|
||||
|
||||
static_assert(util::IsAligned(ThreadStackSize, os::ThreadStackAlignment));
|
||||
alignas(os::ThreadStackAlignment) static constinit inline u8 s_port_stacks[ThreadStackSize * (NumPorts - 1)];
|
||||
|
||||
static constexpr inline bool IsDeferralSupported = !std::same_as<DeferralManagerType, DummyDeferralManager>;
|
||||
using ResumeKey = typename DeferralManagerType::Key;
|
||||
|
||||
static constexpr ALWAYS_INLINE uintptr_t ConvertKeyToMessage(ResumeKey key) {
|
||||
static_assert(sizeof(key) <= sizeof(uintptr_t));
|
||||
static_assert(std::is_trivial<ResumeKey>::value);
|
||||
|
||||
if constexpr (sizeof(key) == sizeof(uintptr_t)) {
|
||||
return std::bit_cast<uintptr_t>(key);
|
||||
} else {
|
||||
uintptr_t converted = 0;
|
||||
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE ResumeKey ConvertMessageToKey(uintptr_t message) {
|
||||
static_assert(sizeof(ResumeKey) <= sizeof(uintptr_t));
|
||||
static_assert(std::is_trivial<ResumeKey>::value);
|
||||
|
||||
if constexpr (sizeof(ResumeKey) == sizeof(uintptr_t)) {
|
||||
return std::bit_cast<ResumeKey>(message);
|
||||
} else {
|
||||
ResumeKey converted = {};
|
||||
std::memcpy(std::addressof(converted), std::addressof(message), sizeof(converted));
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t Ix> requires (Ix < NumPorts)
|
||||
static constexpr inline size_t SessionsPerPortManager = (Ix == NumPorts - 1) ? ((MaxSessions / NumPorts) + MaxSessions % NumPorts)
|
||||
: ((MaxSessions / NumPorts));
|
||||
@@ -324,7 +331,7 @@ namespace ams::tipc {
|
||||
template<typename PortInfo, size_t PortSessions>
|
||||
class PortManagerImpl final : public PortManagerBase {
|
||||
private:
|
||||
tipc::ObjectManager<PortSessions> m_object_manager_impl;
|
||||
tipc::ObjectManager<1 + PortSessions> m_object_manager_impl;
|
||||
public:
|
||||
PortManagerImpl() : PortManagerBase(), m_object_manager_impl() {
|
||||
/* ... */
|
||||
@@ -353,7 +360,6 @@ namespace ams::tipc {
|
||||
PortManagerTuple m_port_managers;
|
||||
PortAllocatorTuple m_port_allocators;
|
||||
os::ThreadType m_port_threads[NumPorts - 1];
|
||||
alignas(os::ThreadStackAlignment) u8 m_port_stacks[ThreadStackSize * (NumPorts - 1)];
|
||||
private:
|
||||
template<size_t Ix>
|
||||
ALWAYS_INLINE auto &GetPortManager() {
|
||||
@@ -378,7 +384,7 @@ namespace ams::tipc {
|
||||
template<size_t Ix>
|
||||
void InitializePortThread(s32 priority) {
|
||||
/* Create the thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(m_port_threads + Ix, &LoopAutoForPortThreadFunction<Ix>, this, m_port_stacks + Ix, ThreadStackSize, priority));
|
||||
R_ABORT_UNLESS(os::CreateThread(m_port_threads + Ix, &LoopAutoForPortThreadFunction<Ix>, this, s_port_stacks + Ix, ThreadStackSize, priority));
|
||||
|
||||
/* Start the thread. */
|
||||
os::StartThread(m_port_threads + Ix);
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace ams {
|
||||
StackFrame cur_frame;
|
||||
svc::lp64::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_fp)) && (mem_info.perm & Perm_R) == Perm_R) {
|
||||
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_fp)) && (mem_info.perm & svc::MemoryPermission_Read) == svc::MemoryPermission_Read) {
|
||||
std::memcpy(&cur_frame, reinterpret_cast<void *>(cur_fp), sizeof(cur_frame));
|
||||
} else {
|
||||
break;
|
||||
@@ -136,7 +136,7 @@ namespace ams {
|
||||
{
|
||||
svc::lp64::MemoryInfo mem_info;
|
||||
svc::PageInfo page_info;
|
||||
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), ams_ctx.sp)) && (mem_info.perm & Perm_R) == Perm_R) {
|
||||
if (R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), ams_ctx.sp)) && (mem_info.perm & svc::MemoryPermission_Read) == svc::MemoryPermission_Read) {
|
||||
size_t copy_size = std::min(FatalErrorContext::MaxStackDumpSize, static_cast<size_t>(mem_info.addr + mem_info.size - ams_ctx.sp));
|
||||
ams_ctx.stack_dump_size = copy_size;
|
||||
std::memcpy(ams_ctx.stack_dump, reinterpret_cast<void *>(ams_ctx.sp), copy_size);
|
||||
@@ -153,29 +153,14 @@ namespace ams {
|
||||
::ams::ExceptionHandler(&ams_ctx);
|
||||
}
|
||||
|
||||
inline NORETURN void AbortImpl() {
|
||||
/* Just perform a data abort. */
|
||||
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
|
||||
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
|
||||
while (true) {
|
||||
__asm__ __volatile__ (
|
||||
"str %[val], [%[addr]]"
|
||||
:
|
||||
: [val]"r"(val), [addr]"r"(addr)
|
||||
);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
NORETURN void AbortImpl();
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* Redefine abort to trigger these handlers. */
|
||||
void abort();
|
||||
|
||||
/* Redefine C++ exception handlers. Requires wrap linker flag. */
|
||||
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { abort(); __builtin_unreachable(); }
|
||||
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { ::ams::AbortImpl(); __builtin_unreachable(); }
|
||||
WRAP_ABORT_FUNC(__cxa_throw)
|
||||
WRAP_ABORT_FUNC(__cxa_rethrow)
|
||||
WRAP_ABORT_FUNC(__cxa_allocate_exception)
|
||||
@@ -195,9 +180,3 @@ extern "C" {
|
||||
#undef WRAP_ABORT_FUNC
|
||||
|
||||
}
|
||||
|
||||
/* Custom abort handler, so that std::abort will trigger these. */
|
||||
void abort() {
|
||||
ams::AbortImpl();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
extern "C" void NORETURN __real_exit(int rc);
|
||||
|
||||
namespace ams {
|
||||
|
||||
WEAK_SYMBOL void *Malloc(size_t size) {
|
||||
@@ -37,13 +39,43 @@ namespace ams {
|
||||
return std::free(ptr);
|
||||
}
|
||||
|
||||
WEAK_SYMBOL void NORETURN Exit(int rc) {
|
||||
__real_exit(rc);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
NOINLINE NORETURN void AbortImpl() {
|
||||
/* Just perform a data abort. */
|
||||
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
|
||||
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
|
||||
while (true) {
|
||||
__asm__ __volatile__ (
|
||||
"str %[val], [%[addr]]"
|
||||
:
|
||||
: [val]"r"(val), [addr]"r"(addr)
|
||||
);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* Redefine abort to trigger these handlers. */
|
||||
void abort();
|
||||
|
||||
/* Redefine C++ exception handlers. Requires wrap linker flag. */
|
||||
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { abort(); __builtin_unreachable(); }
|
||||
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { ::ams::AbortImpl(); __builtin_unreachable(); }
|
||||
WRAP_ABORT_FUNC(__cxa_pure_virtual)
|
||||
#undef WRAP_ABORT_FUNC
|
||||
|
||||
void NORETURN __wrap_exit(int rc) { ::ams::Exit(rc); __builtin_unreachable(); }
|
||||
|
||||
}
|
||||
|
||||
/* Custom abort handler, so that std::abort will trigger these. */
|
||||
void abort() {
|
||||
ams::AbortImpl();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -387,8 +387,8 @@ namespace ams::boot2 {
|
||||
/* NOTE: Here we work around a race condition in the boot process by ensuring that settings initializes its db. */
|
||||
{
|
||||
/* Connect to set:sys. */
|
||||
sm::ScopedServiceHolder<::setsysInitialize, ::setsysExit> setsys_holder;
|
||||
AMS_ABORT_UNLESS(setsys_holder);
|
||||
R_ABORT_UNLESS(::setsysInitialize());
|
||||
ON_SCOPE_EXIT { ::setsysExit(); };
|
||||
|
||||
/* Retrieve setting from the database. */
|
||||
u8 force_maintenance = 0;
|
||||
@@ -424,9 +424,7 @@ namespace ams::boot2 {
|
||||
InitializeFsHeapForCleanup();
|
||||
|
||||
/* Temporarily initialize fs. */
|
||||
sm::DoWithSession([&] {
|
||||
R_ABORT_UNLESS(fsInitialize());
|
||||
});
|
||||
R_ABORT_UNLESS(fsInitialize());
|
||||
ON_SCOPE_EXIT { fsExit(); };
|
||||
|
||||
/* Wait for the sd card to be available. */
|
||||
|
||||
@@ -19,29 +19,28 @@ namespace ams::crypto {
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_initialized;
|
||||
os::Mutex g_lock(false);
|
||||
constinit bool g_initialized = false;
|
||||
constinit os::SdkMutex g_lock;
|
||||
|
||||
void InitializeCsrng() {
|
||||
AMS_ASSERT(!g_initialized);
|
||||
sm::DoWithSession([&]() {
|
||||
R_ABORT_UNLESS(::csrngInitialize());
|
||||
});
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
R_ABORT_UNLESS(::csrngInitialize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) {
|
||||
{
|
||||
if (AMS_UNLIKELY(!g_initialized)) {
|
||||
std::scoped_lock lk(g_lock);
|
||||
|
||||
if (AMS_UNLIKELY(!g_initialized)) {
|
||||
if (AMS_LIKELY(!g_initialized)) {
|
||||
InitializeCsrng();
|
||||
g_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(csrngGetRandomBytes(dst, dst_size));
|
||||
R_ABORT_UNLESS(::csrngGetRandomBytes(dst, dst_size));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace ams::erpt::srv {
|
||||
|
||||
class Allocator {
|
||||
public:
|
||||
void *operator new(size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new(size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
void *operator new[](size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new[](size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
void *operator new(size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new(size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
void *operator new[](size_t sz) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz); }
|
||||
void *operator new[](size_t sz, size_t algn) noexcept { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast<s32>(algn)); }
|
||||
|
||||
void operator delete(void *p) { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
void operator delete[](void *p) { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
void operator delete(void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
void operator delete[](void *p) noexcept { lmem::FreeToExpHeap(g_heap_handle, p); }
|
||||
};
|
||||
|
||||
inline void *Allocate(size_t sz) {
|
||||
|
||||
@@ -126,4 +126,13 @@ namespace ams::erpt::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Context::ClearContext(CategoryId cat) {
|
||||
/* Make an empty record for the category. */
|
||||
auto record = std::make_unique<ContextRecord>(cat);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Submit the context record. */
|
||||
return SubmitContextRecord(std::move(record));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace ams::erpt::srv {
|
||||
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||
static Result SubmitContextRecord(std::unique_ptr<ContextRecord> record);
|
||||
static Result WriteContextsToReport(Report *report);
|
||||
static Result ClearContext(CategoryId cat);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_reporter.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_forced_shutdown.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
@@ -32,6 +33,8 @@ namespace ams::erpt::srv {
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument());
|
||||
|
||||
SubmitContextForForcedShutdownDetection(ctx, data, data_size);
|
||||
|
||||
return Context::SubmitContext(ctx, data, data_size);
|
||||
}
|
||||
|
||||
@@ -47,8 +50,7 @@ namespace ams::erpt::srv {
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(meta_size == 0 || meta_size == sizeof(ReportMetaData), erpt::ResultInvalidArgument());
|
||||
|
||||
Reporter reporter(report_type, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0, result);
|
||||
R_TRY(reporter.CreateReport());
|
||||
R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0));
|
||||
|
||||
ManagerImpl::NotifyAll();
|
||||
|
||||
@@ -70,12 +72,12 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdatePowerOnTime() {
|
||||
Reporter::UpdatePowerOnTime();
|
||||
/* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdateAwakeTime() {
|
||||
Reporter::UpdateAwakeTime();
|
||||
/* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
@@ -148,8 +150,7 @@ namespace ams::erpt::srv {
|
||||
R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument());
|
||||
R_UNLESS(num_attachments <= AttachmentsPerReportMax, erpt::ResultInvalidArgument());
|
||||
|
||||
Reporter reporter(report_type, ctx, data, data_size, nullptr, attachments, num_attachments, result);
|
||||
R_TRY(reporter.CreateReport());
|
||||
R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, nullptr, attachments, num_attachments));
|
||||
|
||||
ManagerImpl::NotifyAll();
|
||||
|
||||
@@ -161,22 +162,20 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
|
||||
Result ContextImpl::RegisterRunningApplet(ncm::ProgramId program_id) {
|
||||
/* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */
|
||||
return ResultSuccess();
|
||||
return Reporter::RegisterRunningApplet(program_id);
|
||||
}
|
||||
|
||||
Result ContextImpl::UnregisterRunningApplet(ncm::ProgramId program_id) {
|
||||
/* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */
|
||||
return ResultSuccess();
|
||||
return Reporter::UnregisterRunningApplet(program_id);
|
||||
}
|
||||
|
||||
Result ContextImpl::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpanType duration) {
|
||||
/* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */
|
||||
return ResultSuccess();
|
||||
return Reporter::UpdateAppletSuspendedDuration(program_id, duration);
|
||||
}
|
||||
|
||||
Result ContextImpl::InvalidateForcedShutdownDetection() {
|
||||
/* TODO: For greater accuracy, we should support the forced shutdown detection feature added in 12.0.0. */
|
||||
/* NOTE: Nintendo does not check the result here. */
|
||||
erpt::srv::InvalidateForcedShutdownDetection();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace ams::erpt::srv {
|
||||
explicit ContextRecord(CategoryId category, u32 array_buf_size = ArrayBufferSizeDefault);
|
||||
~ContextRecord();
|
||||
|
||||
const ContextEntry *GetContextEntryPtr() const {
|
||||
return std::addressof(this->ctx);
|
||||
}
|
||||
|
||||
Result Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size);
|
||||
|
||||
Result Add(FieldId field_id, bool value_bool);
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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_forced_shutdown.hpp"
|
||||
#include "erpt_srv_context.hpp"
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
#include "erpt_srv_reporter.hpp"
|
||||
#include "erpt_srv_stream.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 ForcedShutdownContextBufferSize = 1_KB;
|
||||
|
||||
constexpr u32 ForcedShutdownContextVersion = 1;
|
||||
|
||||
struct ForcedShutdownContextHeader {
|
||||
u32 version;
|
||||
u32 num_contexts;
|
||||
};
|
||||
static_assert(sizeof(ForcedShutdownContextHeader) == 8);
|
||||
|
||||
struct ForcedShutdownContextEntry {
|
||||
u32 version;
|
||||
CategoryId category;
|
||||
u32 field_count;
|
||||
u32 array_buffer_size;
|
||||
};
|
||||
static_assert(sizeof(ForcedShutdownContextEntry) == 16);
|
||||
|
||||
os::Event g_forced_shutdown_update_event(os::EventClearMode_ManualClear);
|
||||
|
||||
constinit ContextEntry g_forced_shutdown_contexts[] = {
|
||||
{ .category = CategoryId_RunningApplicationInfo, },
|
||||
{ .category = CategoryId_RunningAppletInfo, },
|
||||
{ .category = CategoryId_FocusedAppletHistoryInfo, },
|
||||
};
|
||||
|
||||
bool IsForceShutdownDetected() {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), ForcedShutdownContextFileName));
|
||||
}
|
||||
|
||||
Result CreateForcedShutdownContext() {
|
||||
/* Create the context. */
|
||||
{
|
||||
/* Create the stream. */
|
||||
Stream stream;
|
||||
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, 0));
|
||||
|
||||
/* Write a context header. */
|
||||
const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = 0, };
|
||||
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(header)), sizeof(header)));
|
||||
}
|
||||
|
||||
/* Commit the context. */
|
||||
R_TRY(Stream::CommitStream());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result CreateReportForForcedShutdown() {
|
||||
/* Create a new context record. */
|
||||
/* NOTE: Nintendo does not check that this allocation succeeds. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfo);
|
||||
|
||||
/* Create error code for the report. */
|
||||
char error_code_str[err::ErrorCode::StringLengthMax];
|
||||
err::GetErrorCodeString(error_code_str, sizeof(error_code_str), err::ConvertResultToErrorCode(err::ResultForcedShutdownDetected()));
|
||||
|
||||
/* Add error code to the context. */
|
||||
R_TRY(record->Add(FieldId_ErrorCode, error_code_str, std::strlen(error_code_str)));
|
||||
|
||||
/* Create report. */
|
||||
R_TRY(Reporter::CreateReport(ReportType_Invisible, ResultSuccess(), std::move(record), nullptr, nullptr, 0));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result LoadForcedShutdownContext() {
|
||||
/* Create the stream to read the context. */
|
||||
Stream stream;
|
||||
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Read, ForcedShutdownContextBufferSize));
|
||||
|
||||
/* Read the header. */
|
||||
u32 read_size;
|
||||
ForcedShutdownContextHeader header;
|
||||
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(header)), sizeof(header)));
|
||||
|
||||
/* Validate the header. */
|
||||
R_SUCCEED_IF(read_size != sizeof(header));
|
||||
R_SUCCEED_IF(ForcedShutdownContextVersion);
|
||||
R_SUCCEED_IF(header.num_contexts == 0);
|
||||
|
||||
/* Read out the contexts. */
|
||||
for (u32 i = 0; i < header.num_contexts; ++i) {
|
||||
/* Read the context entry header. */
|
||||
ForcedShutdownContextEntry entry_header;
|
||||
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(entry_header)), sizeof(entry_header)));
|
||||
|
||||
if (read_size != sizeof(entry_header)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry_header.field_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read the saved data into a context entry. */
|
||||
ContextEntry ctx = {
|
||||
.version = entry_header.version,
|
||||
.field_count = entry_header.field_count,
|
||||
.category = entry_header.category,
|
||||
};
|
||||
|
||||
/* Check that the field count is valid. */
|
||||
AMS_ABORT_UNLESS(entry_header.field_count <= util::size(ctx.fields));
|
||||
|
||||
/* Read the fields. */
|
||||
R_TRY(stream.ReadStream(std::addressof(read_size), reinterpret_cast<u8 *>(std::addressof(ctx.fields)), entry_header.field_count * sizeof(ctx.fields[0])));
|
||||
if (read_size != entry_header.field_count * sizeof(ctx.fields[0])) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Allocate an array buffer. */
|
||||
u8 *array_buffer = static_cast<u8 *>(Allocate(entry_header.array_buffer_size));
|
||||
if (array_buffer == nullptr) {
|
||||
break;
|
||||
}
|
||||
ON_SCOPE_EXIT { Deallocate(array_buffer); };
|
||||
|
||||
/* Read the array buffer data. */
|
||||
R_TRY(stream.ReadStream(std::addressof(read_size), array_buffer, entry_header.array_buffer_size));
|
||||
if (read_size != entry_header.array_buffer_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create a record for the context. */
|
||||
auto record = std::make_unique<ContextRecord>();
|
||||
if (record == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize the record. */
|
||||
R_TRY(record->Initialize(std::addressof(ctx), array_buffer, entry_header.array_buffer_size));
|
||||
|
||||
/* Submit the record. */
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
u32 GetForcedShutdownContextCount() {
|
||||
u32 count = 0;
|
||||
for (const auto &ctx : g_forced_shutdown_contexts) {
|
||||
if (ctx.field_count != 0) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Result SaveForcedShutdownContextImpl() {
|
||||
/* Save context to file. */
|
||||
{
|
||||
/* Create the stream to write the context. */
|
||||
Stream stream;
|
||||
R_TRY(stream.OpenStream(ForcedShutdownContextFileName, StreamMode_Write, ForcedShutdownContextBufferSize));
|
||||
|
||||
/* Write a context header. */
|
||||
const ForcedShutdownContextHeader header = { .version = ForcedShutdownContextVersion, .num_contexts = GetForcedShutdownContextCount(), };
|
||||
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(header)), sizeof(header)));
|
||||
|
||||
/* Write each context. */
|
||||
for (const auto &ctx : g_forced_shutdown_contexts) {
|
||||
/* If the context has no fields, continue. */
|
||||
if (ctx.field_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write a context entry header. */
|
||||
const ForcedShutdownContextEntry entry_header = {
|
||||
.version = ctx.version,
|
||||
.category = ctx.category,
|
||||
.field_count = ctx.field_count,
|
||||
.array_buffer_size = ctx.array_buffer_size,
|
||||
};
|
||||
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(std::addressof(entry_header)), sizeof(entry_header)));
|
||||
|
||||
/* Write all fields. */
|
||||
for (u32 i = 0; i < ctx.field_count; ++i) {
|
||||
R_TRY(stream.WriteStream(reinterpret_cast<const u8 *>(ctx.fields + i), sizeof(ctx.fields[0])));
|
||||
}
|
||||
|
||||
/* Write the array buffer. */
|
||||
R_TRY(stream.WriteStream(ctx.array_buffer, ctx.array_buffer_size));
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit the context. */
|
||||
R_TRY(Stream::CommitStream());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
os::Event *GetForcedShutdownUpdateEvent() {
|
||||
return std::addressof(g_forced_shutdown_update_event);
|
||||
}
|
||||
|
||||
void InitializeForcedShutdownDetection() {
|
||||
/* Check if the forced shutdown context exists; if it doesn't, we should create an empty one. */
|
||||
if (!IsForceShutdownDetected()) {
|
||||
/* NOTE: Nintendo does not check result here. */
|
||||
CreateForcedShutdownContext();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Load the forced shutdown context. */
|
||||
/* NOTE: Nintendo does not check that this succeeds. */
|
||||
LoadForcedShutdownContext();
|
||||
|
||||
/* Create report for the forced shutdown. */
|
||||
/* NOTE: Nintendo does not check that this succeeds. */
|
||||
CreateReportForForcedShutdown();
|
||||
|
||||
/* Clear the forced shutdown categories. */
|
||||
/* NOTE: Nintendo does not check that this succeeds. */
|
||||
Context::ClearContext(CategoryId_RunningApplicationInfo);
|
||||
Context::ClearContext(CategoryId_RunningAppletInfo);
|
||||
Context::ClearContext(CategoryId_FocusedAppletHistoryInfo);
|
||||
|
||||
/* Save the forced shutdown context. */
|
||||
/* NOTE: Nintendo does not check that this succeeds. */
|
||||
SaveForcedShutdownContext();
|
||||
}
|
||||
|
||||
void FinalizeForcedShutdownDetection() {
|
||||
/* Try to delete the context. */
|
||||
const Result result = Stream::DeleteStream(ForcedShutdownContextFileName);
|
||||
if (!fs::ResultPathNotFound::Includes(result)) {
|
||||
/* We must have succeeded, if the file existed. */
|
||||
R_ABORT_UNLESS(result);
|
||||
|
||||
/* Commit the deletion. */
|
||||
R_ABORT_UNLESS(Stream::CommitStream());
|
||||
}
|
||||
}
|
||||
|
||||
void SaveForcedShutdownContext() {
|
||||
/* NOTE: Nintendo does not check that saving the report succeeds. */
|
||||
SaveForcedShutdownContextImpl();
|
||||
}
|
||||
|
||||
void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
||||
/* If the context entry matches one of our tracked categories, update our stored category. */
|
||||
for (auto &ctx : g_forced_shutdown_contexts) {
|
||||
/* Check for a match. */
|
||||
if (ctx.category != entry->category) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we have an existing array buffer, free it. */
|
||||
if (ctx.array_buffer != nullptr) {
|
||||
Deallocate(ctx.array_buffer);
|
||||
ctx.array_buffer = nullptr;
|
||||
ctx.array_buffer_size = 0;
|
||||
ctx.array_free_count = 0;
|
||||
}
|
||||
|
||||
/* Copy in the context. */
|
||||
ctx = *entry;
|
||||
|
||||
/* Add the submitted data. */
|
||||
if (data != nullptr && data_size > 0) {
|
||||
/* Allocate new array buffer. */
|
||||
ctx.array_buffer = static_cast<u8 *>(Allocate(data_size));
|
||||
if (ctx.array_buffer == nullptr) {
|
||||
/* We failed to allocate; this is okay, but clear our field count. */
|
||||
ctx.field_count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy in the data. */
|
||||
std::memcpy(ctx.array_buffer, data, data_size);
|
||||
|
||||
/* Set buffer extents. */
|
||||
ctx.array_buffer_size = data_size;
|
||||
ctx.array_free_count = 0;
|
||||
} else {
|
||||
ctx.array_buffer = nullptr;
|
||||
ctx.array_buffer_size = 0;
|
||||
ctx.array_free_count = 0;
|
||||
}
|
||||
|
||||
/* Signal, to notify that we had an update. */
|
||||
g_forced_shutdown_update_event.Signal();
|
||||
|
||||
/* We're done processing, since we found a match. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result InvalidateForcedShutdownDetection() {
|
||||
/* Delete the forced shutdown context. */
|
||||
R_TRY(Stream::DeleteStream(ForcedShutdownContextFileName));
|
||||
|
||||
/* Commit the deletion. */
|
||||
R_TRY(Stream::CommitStream());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 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 {
|
||||
|
||||
os::Event *GetForcedShutdownUpdateEvent();
|
||||
|
||||
void InitializeForcedShutdownDetection();
|
||||
void FinalizeForcedShutdownDetection();
|
||||
|
||||
void SaveForcedShutdownContext();
|
||||
|
||||
void SubmitContextForForcedShutdownDetection(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||
|
||||
Result InvalidateForcedShutdownDetection();
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "erpt_srv_reporter.hpp"
|
||||
#include "erpt_srv_journal.hpp"
|
||||
#include "erpt_srv_service.hpp"
|
||||
#include "erpt_srv_forced_shutdown.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
@@ -89,10 +90,17 @@ namespace ams::erpt::srv {
|
||||
|
||||
Journal::Restore();
|
||||
|
||||
Reporter::UpdatePowerOnTime();
|
||||
Reporter::UpdateAwakeTime();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result InitializeAndStartService() {
|
||||
/* Initialize forced shutdown detection. */
|
||||
/* NOTE: Nintendo does not check error code here. */
|
||||
InitializeForcedShutdownDetection();
|
||||
|
||||
return InitializeService();
|
||||
}
|
||||
|
||||
@@ -128,7 +136,15 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
return WaitService();
|
||||
/* Get the update event. */
|
||||
os::Event *event = GetForcedShutdownUpdateEvent();
|
||||
|
||||
/* Forever wait, saving any updates. */
|
||||
while (true) {
|
||||
event->Wait();
|
||||
event->Clear();
|
||||
SaveForcedShutdownContext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,61 @@ namespace ams::erpt::srv {
|
||||
constinit os::SdkMutex g_limit_mutex;
|
||||
constinit bool g_submitted_limit = false;
|
||||
|
||||
class AppletActiveTimeInfoList {
|
||||
private:
|
||||
struct AppletActiveTimeInfo {
|
||||
ncm::ProgramId program_id;
|
||||
os::Tick register_tick;
|
||||
TimeSpan suspended_duration;
|
||||
};
|
||||
static constexpr AppletActiveTimeInfo InvalidAppletActiveTimeInfo = { ncm::InvalidProgramId, os::Tick{}, TimeSpan::FromNanoSeconds(0) };
|
||||
private:
|
||||
std::array<AppletActiveTimeInfo, 8> m_list;
|
||||
public:
|
||||
constexpr AppletActiveTimeInfoList() : m_list{InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo} {
|
||||
m_list.fill(InvalidAppletActiveTimeInfo);
|
||||
}
|
||||
public:
|
||||
void Register(ncm::ProgramId program_id) {
|
||||
/* Find an unused entry. */
|
||||
auto entry = util::range::find_if(m_list, [](const AppletActiveTimeInfo &info) { return info.program_id == ncm::InvalidProgramId; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Create the entry. */
|
||||
*entry = { program_id, os::GetSystemTick(), TimeSpan::FromNanoSeconds(0) };
|
||||
}
|
||||
|
||||
void Unregister(ncm::ProgramId program_id) {
|
||||
/* Find a matching entry. */
|
||||
auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Clear the entry. */
|
||||
*entry = InvalidAppletActiveTimeInfo;
|
||||
}
|
||||
|
||||
void UpdateSuspendedDuration(ncm::ProgramId program_id, TimeSpan suspended_duration) {
|
||||
/* Find a matching entry. */
|
||||
auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
AMS_ASSERT(entry != m_list.end());
|
||||
|
||||
/* Set the suspended duration. */
|
||||
entry->suspended_duration = suspended_duration;
|
||||
}
|
||||
|
||||
std::optional<TimeSpan> GetActiveDuration(ncm::ProgramId program_id) const {
|
||||
/* Try to find a matching entry. */
|
||||
const auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; });
|
||||
if (entry != m_list.end()) {
|
||||
return (os::GetSystemTick() - entry->register_tick).ToTimeSpan() - entry->suspended_duration;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constinit AppletActiveTimeInfoList g_applet_active_time_info_list;
|
||||
|
||||
Result PullErrorContext(size_t *out_total_size, size_t *out_size, void *dst, size_t dst_size, const err::ContextDescriptor &descriptor, Result result) {
|
||||
s32 unk0;
|
||||
u32 total_size, size;
|
||||
@@ -170,127 +225,299 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Result ValidateCreateReportContext(const ContextEntry *ctx) {
|
||||
R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
|
||||
R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
|
||||
|
||||
Reporter::Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, Result ctx_r)
|
||||
: type(type), ctx(ctx), data(data), data_size(data_size), meta(meta), attachments(attachments), num_attachments(num_attachments), occurrence_tick(), ctx_result(ctx_r)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
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());
|
||||
|
||||
Result Reporter::CreateReport() {
|
||||
R_TRY(this->ValidateReportContext());
|
||||
R_TRY(this->CollectUniqueReportFields());
|
||||
R_TRY(this->SubmitReportDefaults());
|
||||
R_TRY(this->SubmitReportContexts());
|
||||
R_TRY(this->LinkAttachments());
|
||||
R_TRY(this->CreateReportFile());
|
||||
|
||||
this->SaveSyslogReportIfRequired();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::ValidateReportContext() {
|
||||
R_UNLESS(this->ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
|
||||
R_UNLESS(this->ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
|
||||
|
||||
const bool found_error_code = util::range::any_of(MakeSpan(this->ctx->fields, this->ctx->field_count), [] (const FieldEntry &entry) {
|
||||
return entry.id == FieldId_ErrorCode;
|
||||
});
|
||||
R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::CollectUniqueReportFields() {
|
||||
this->occurrence_tick = os::GetSystemTick();
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
this->steady_clock_internal_offset_seconds = time::GetStandardSteadyClockInternalOffset().GetSeconds();
|
||||
} else {
|
||||
this->steady_clock_internal_offset_seconds = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
this->report_id.uuid = util::GenerateUuid();
|
||||
this->report_id.uuid.ToString(this->identifier_str, sizeof(this->identifier_str));
|
||||
if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(this->timestamp_network)))) {
|
||||
this->timestamp_network = {0};
|
||||
|
||||
Result SubmitReportDefaults(const ContextEntry *ctx) {
|
||||
AMS_ASSERT(ctx->category == CategoryId_ErrorInfo);
|
||||
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoDefaults);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
bool found_abort_flag = false, found_syslog_flag = false;
|
||||
for (u32 i = 0; i < ctx->field_count; i++) {
|
||||
if (ctx->fields[i].id == FieldId_AbortFlag) {
|
||||
found_abort_flag = true;
|
||||
}
|
||||
if (ctx->fields[i].id == FieldId_HasSyslogFlag) {
|
||||
found_syslog_flag = true;
|
||||
}
|
||||
if (found_abort_flag && found_syslog_flag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_abort_flag) {
|
||||
record->Add(FieldId_AbortFlag, false);
|
||||
}
|
||||
|
||||
if (!found_syslog_flag) {
|
||||
record->Add(FieldId_HasSyslogFlag, true);
|
||||
}
|
||||
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(this->steady_clock_current_timepoint)));
|
||||
R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(this->timestamp_user)));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::SubmitReportDefaults() {
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoDefaults);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
void SaveSyslogReportIfRequired(const ContextEntry *ctx, const ReportId &report_id) {
|
||||
bool needs_save_syslog = true;
|
||||
for (u32 i = 0; i < ctx->field_count; i++) {
|
||||
static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool);
|
||||
if (ctx->fields[i].id == FieldId_HasSyslogFlag && !ctx->fields[i].value_bool) {
|
||||
needs_save_syslog = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool found_abort_flag = false, found_syslog_flag = false;
|
||||
for (u32 i = 0; i < this->ctx->field_count; i++) {
|
||||
if (this->ctx->fields[i].id == FieldId_AbortFlag) {
|
||||
found_abort_flag = true;
|
||||
}
|
||||
if (this->ctx->fields[i].id == FieldId_HasSyslogFlag) {
|
||||
found_syslog_flag = true;
|
||||
}
|
||||
if (found_abort_flag && found_syslog_flag) {
|
||||
break;
|
||||
if (needs_save_syslog) {
|
||||
/* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data report_id. */
|
||||
/* We will not send report ids to srepo:u. */
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_abort_flag) {
|
||||
record->Add(FieldId_AbortFlag, false);
|
||||
void SubmitAppletActiveDurationForCrashReport(const ContextEntry *error_info_ctx, const void *data, u32 data_size, ContextRecord *error_info_auto_record) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(error_info_ctx != nullptr);
|
||||
AMS_ASSERT(error_info_ctx->category == CategoryId_ErrorInfo);
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(error_info_auto_record != nullptr);
|
||||
|
||||
/* Find the program id entry. */
|
||||
const auto fields_span = MakeSpan(error_info_ctx->fields, error_info_ctx->field_count);
|
||||
const auto program_id_entry = util::range::find_if(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_ProgramId; });
|
||||
if (program_id_entry == fields_span.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check that the report has abort flag set. */
|
||||
AMS_ASSERT(util::range::any_of(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_AbortFlag && entry.value_bool; }));
|
||||
|
||||
/* Check that the program id's value is a string. */
|
||||
AMS_ASSERT(program_id_entry->type == FieldType_String);
|
||||
|
||||
/* Check that the program id's length is valid/in bounds. */
|
||||
const auto program_id_ofs = program_id_entry->value_array.start_idx;
|
||||
const auto program_id_len = program_id_entry->value_array.size;
|
||||
AMS_ASSERT(16 <= program_id_len && program_id_len <= 17);
|
||||
AMS_ASSERT(program_id_ofs + program_id_len <= data_size);
|
||||
|
||||
/* Get the program id string. */
|
||||
char program_id_str[17];
|
||||
std::memcpy(program_id_str, static_cast<const u8 *>(data) + program_id_ofs, std::min<size_t>(program_id_len, sizeof(program_id_str)));
|
||||
program_id_str[sizeof(program_id_str) - 1] = '\x00';
|
||||
|
||||
/* Convert the string to an integer. */
|
||||
char *end_ptr = nullptr;
|
||||
const ncm::ProgramId program_id = { std::strtoull(program_id_str, std::addressof(end_ptr), 16) };
|
||||
AMS_ASSERT(*end_ptr == '\x00');
|
||||
|
||||
/* Get the active duration. */
|
||||
const auto active_duration = g_applet_active_time_info_list.GetActiveDuration(program_id);
|
||||
if (!active_duration.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add the active applet time. */
|
||||
const auto result = error_info_auto_record->Add(FieldId_AppletTotalActiveTime, (*active_duration).GetSeconds());
|
||||
R_ASSERT(result);
|
||||
}
|
||||
|
||||
if (!found_syslog_flag) {
|
||||
record->Add(FieldId_HasSyslogFlag, true);
|
||||
Result LinkAttachments(const ReportId &report_id, const AttachmentId *attachments, u32 num_attachments) {
|
||||
for (u32 i = 0; i < num_attachments; i++) {
|
||||
R_TRY(JournalForAttachments::SetOwner(attachments[i], report_id));
|
||||
}
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
Result CreateReportFile(const ReportId &report_id, ReportType type, const ReportMetaData *meta, u32 num_attachments, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network, bool redirect_new_reports) {
|
||||
/* Define journal record deleter. */
|
||||
struct JournalRecordDeleter {
|
||||
void operator()(JournalRecord<ReportInfo> *record) {
|
||||
if (record != nullptr) {
|
||||
if (record->RemoveReference()) {
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Make a journal record. */
|
||||
auto record = std::unique_ptr<JournalRecord<ReportInfo>, JournalRecordDeleter>{new JournalRecord<ReportInfo>, JournalRecordDeleter{}};
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
record->AddReference();
|
||||
|
||||
record->info.type = type;
|
||||
record->info.id = report_id;
|
||||
record->info.flags = erpt::srv::MakeNoReportFlags();
|
||||
record->info.timestamp_user = timestamp_user;
|
||||
record->info.timestamp_network = timestamp_network;
|
||||
if (meta != nullptr) {
|
||||
record->info.meta_data = *meta;
|
||||
}
|
||||
if (num_attachments > 0) {
|
||||
record->info.flags.Set<ReportFlag::HasAttachment>();
|
||||
}
|
||||
|
||||
auto report = std::make_unique<Report>(record.get(), redirect_new_reports);
|
||||
R_UNLESS(report != nullptr, erpt::ResultOutOfMemory());
|
||||
auto report_guard = SCOPE_GUARD { report->Delete(); };
|
||||
|
||||
R_TRY(Context::WriteContextsToReport(report.get()));
|
||||
R_TRY(report->GetSize(std::addressof(record->info.report_size)));
|
||||
|
||||
if (!redirect_new_reports) {
|
||||
/* If we're not redirecting new reports, then we want to store the report in the journal. */
|
||||
R_TRY(Journal::Store(record.get()));
|
||||
} 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(Journal::Commit());
|
||||
|
||||
report_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) {
|
||||
g_applet_active_time_info_list.Register(program_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::SubmitReportContexts() {
|
||||
Result Reporter::UnregisterRunningApplet(ncm::ProgramId program_id) {
|
||||
g_applet_active_time_info_list.Unregister(program_id);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration) {
|
||||
g_applet_active_time_info_list.UpdateSuspendedDuration(program_id, duration);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments) {
|
||||
/* Create a context record for the report. */
|
||||
auto record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoAuto);
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Handle error context. */
|
||||
if (R_FAILED(this->ctx_result)) {
|
||||
SubmitErrorContext(record.get(), this->ctx_result);
|
||||
/* Initialize the record. */
|
||||
R_TRY(record->Initialize(ctx, data, data_size));
|
||||
|
||||
/* Create the report. */
|
||||
return CreateReport(type, ctx_result, std::move(record), meta, attachments, num_attachments);
|
||||
}
|
||||
|
||||
Result Reporter::CreateReport(ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments) {
|
||||
/* Clear the automatic categories, when we're done with our report. */
|
||||
ON_SCOPE_EXIT {
|
||||
Context::ClearContext(CategoryId_ErrorInfo);
|
||||
Context::ClearContext(CategoryId_ErrorInfoAuto);
|
||||
Context::ClearContext(CategoryId_ErrorInfoDefaults);
|
||||
};
|
||||
|
||||
/* Get the context entry pointer. */
|
||||
const ContextEntry *ctx = record->GetContextEntryPtr();
|
||||
|
||||
/* Validate the context. */
|
||||
R_TRY(ValidateCreateReportContext(ctx));
|
||||
|
||||
/* Submit report defaults. */
|
||||
R_TRY(SubmitReportDefaults(ctx));
|
||||
|
||||
/* Generate report id. */
|
||||
const ReportId report_id = { .uuid = util::GenerateUuid() };
|
||||
|
||||
/* Get posix timestamps. */
|
||||
time::PosixTime timestamp_user;
|
||||
time::PosixTime timestamp_network;
|
||||
R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(timestamp_user)));
|
||||
if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(timestamp_network)))) {
|
||||
timestamp_network = {};
|
||||
}
|
||||
|
||||
record->Add(FieldId_OsVersion, s_os_version, strnlen(s_os_version, sizeof(s_os_version)));
|
||||
record->Add(FieldId_PrivateOsVersion, s_private_os_version, strnlen(s_private_os_version, sizeof(s_private_os_version)));
|
||||
record->Add(FieldId_SerialNumber, s_serial_number, strnlen(s_serial_number, sizeof(s_serial_number)));
|
||||
record->Add(FieldId_ReportIdentifier, this->identifier_str, sizeof(this->identifier_str));
|
||||
record->Add(FieldId_OccurrenceTimestamp, this->timestamp_user.value);
|
||||
record->Add(FieldId_OccurrenceTimestampNet, this->timestamp_network.value);
|
||||
record->Add(FieldId_ReportVisibilityFlag, this->type == ReportType_Visible);
|
||||
record->Add(FieldId_OccurrenceTick, this->occurrence_tick.GetInt64Value());
|
||||
record->Add(FieldId_SteadyClockInternalOffset, this->steady_clock_internal_offset_seconds);
|
||||
record->Add(FieldId_SteadyClockCurrentTimePointValue, this->steady_clock_current_timepoint.value);
|
||||
/* Save syslog report, if required. */
|
||||
SaveSyslogReportIfRequired(ctx, report_id);
|
||||
|
||||
/* Submit report contexts. */
|
||||
R_TRY(SubmitReportContexts(report_id, type, ctx_result, std::move(record), timestamp_user, timestamp_network));
|
||||
|
||||
/* Link attachments to the report. */
|
||||
R_TRY(LinkAttachments(report_id, attachments, num_attachments));
|
||||
|
||||
/* Create the report file. */
|
||||
R_TRY(CreateReportFile(report_id, type, meta, num_attachments, timestamp_user, timestamp_network, s_redirect_new_reports));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network) {
|
||||
/* Create automatic record. */
|
||||
auto auto_record = std::make_unique<ContextRecord>(CategoryId_ErrorInfoAuto, 0x300);
|
||||
R_UNLESS(auto_record != nullptr, erpt::ResultOutOfMemory());
|
||||
|
||||
/* Handle error context. */
|
||||
if (R_FAILED(ctx_result)) {
|
||||
SubmitErrorContext(auto_record.get(), ctx_result);
|
||||
}
|
||||
|
||||
/* Collect unique report fields. */
|
||||
char identifier_str[0x40];
|
||||
report_id.uuid.ToString(identifier_str, sizeof(identifier_str));
|
||||
|
||||
const auto occurrence_tick = os::GetSystemTick();
|
||||
const s64 steady_clock_internal_offset_seconds = (hos::GetVersion() >= hos::Version_5_0_0) ? time::GetStandardSteadyClockInternalOffset().GetSeconds() : 0;
|
||||
|
||||
time::SteadyClockTimePoint steady_clock_current_timepoint;
|
||||
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint)));
|
||||
|
||||
/* Add automatic fields. */
|
||||
auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version)));
|
||||
auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version)));
|
||||
auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number)));
|
||||
auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str)));
|
||||
auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value);
|
||||
auto_record->Add(FieldId_OccurrenceTimestampNet, timestamp_network.value);
|
||||
auto_record->Add(FieldId_ReportVisibilityFlag, type == ReportType_Visible);
|
||||
auto_record->Add(FieldId_OccurrenceTick, occurrence_tick.GetInt64Value());
|
||||
auto_record->Add(FieldId_SteadyClockInternalOffset, steady_clock_internal_offset_seconds);
|
||||
auto_record->Add(FieldId_SteadyClockCurrentTimePointValue, steady_clock_current_timepoint.value);
|
||||
auto_record->Add(FieldId_ElapsedTimeSincePowerOn, (occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds());
|
||||
auto_record->Add(FieldId_ElapsedTimeSinceLastAwake, (occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds());
|
||||
|
||||
if (s_initial_launch_settings_completion_time) {
|
||||
s64 elapsed_seconds;
|
||||
if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, this->steady_clock_current_timepoint))) {
|
||||
record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds);
|
||||
if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, steady_clock_current_timepoint))) {
|
||||
auto_record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds);
|
||||
}
|
||||
}
|
||||
|
||||
if (s_power_on_time) {
|
||||
record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds());
|
||||
}
|
||||
|
||||
if (s_awake_time) {
|
||||
record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds());
|
||||
}
|
||||
|
||||
if (s_application_launch_time) {
|
||||
record->Add(FieldId_ApplicationAliveTime, (this->occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds());
|
||||
auto_record->Add(FieldId_ApplicationAliveTime, (occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds());
|
||||
}
|
||||
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
/* Submit applet active duration information. */
|
||||
{
|
||||
const auto *error_info_ctx = record->GetContextEntryPtr();
|
||||
SubmitAppletActiveDurationForCrashReport(error_info_ctx, error_info_ctx->array_buffer, error_info_ctx->array_buffer_size - error_info_ctx->array_free_count, auto_record.get());
|
||||
}
|
||||
|
||||
R_TRY(Context::SubmitContext(this->ctx, this->data, this->data_size));
|
||||
/* Submit the auto record. */
|
||||
R_TRY(Context::SubmitContextRecord(std::move(auto_record)));
|
||||
|
||||
/* Submit the info record. */
|
||||
R_TRY(Context::SubmitContextRecord(std::move(record)));
|
||||
|
||||
/* Submit context for resource limits. */
|
||||
SubmitResourceLimitContexts();
|
||||
@@ -298,77 +525,4 @@ namespace ams::erpt::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::LinkAttachments() {
|
||||
for (u32 i = 0; i < this->num_attachments; i++) {
|
||||
R_TRY(JournalForAttachments::SetOwner(this->attachments[i], this->report_id));
|
||||
}
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result Reporter::CreateReportFile() {
|
||||
/* Define journal record deleter. */
|
||||
struct JournalRecordDeleter {
|
||||
void operator()(JournalRecord<ReportInfo> *record) {
|
||||
if (record != nullptr) {
|
||||
if (record->RemoveReference()) {
|
||||
delete record;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Make a journal record. */
|
||||
auto record = std::unique_ptr<JournalRecord<ReportInfo>, JournalRecordDeleter>{new JournalRecord<ReportInfo>, JournalRecordDeleter{}};
|
||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||
record->AddReference();
|
||||
|
||||
record->info.type = this->type;
|
||||
record->info.id = this->report_id;
|
||||
record->info.flags = erpt::srv::MakeNoReportFlags();
|
||||
record->info.timestamp_user = this->timestamp_user;
|
||||
record->info.timestamp_network = this->timestamp_network;
|
||||
if (this->meta != nullptr) {
|
||||
record->info.meta_data = *this->meta;
|
||||
}
|
||||
if (this->num_attachments > 0) {
|
||||
record->info.flags.Set<ReportFlag::HasAttachment>();
|
||||
}
|
||||
|
||||
auto report = std::make_unique<Report>(record.get(), s_redirect_new_reports);
|
||||
R_UNLESS(report != nullptr, erpt::ResultOutOfMemory());
|
||||
auto report_guard = SCOPE_GUARD { report->Delete(); };
|
||||
|
||||
R_TRY(Context::WriteContextsToReport(report.get()));
|
||||
R_TRY(report->GetSize(std::addressof(record->info.report_size)));
|
||||
|
||||
if (!s_redirect_new_reports) {
|
||||
/* If we're not redirecting new reports, then we want to store the report in the journal. */
|
||||
R_TRY(Journal::Store(record.get()));
|
||||
} 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(this->report_id));
|
||||
}
|
||||
|
||||
R_TRY(Journal::Commit());
|
||||
|
||||
report_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void Reporter::SaveSyslogReportIfRequired() {
|
||||
bool needs_save_syslog = true;
|
||||
for (u32 i = 0; i < this->ctx->field_count; i++) {
|
||||
static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool);
|
||||
if (this->ctx->fields[i].id == FieldId_HasSyslogFlag && (this->ctx->fields[i].value_bool == false)) {
|
||||
needs_save_syslog = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_save_syslog) {
|
||||
/* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data this->report_id. */
|
||||
/* We will not send report ids to srepo:u. */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "erpt_srv_context_record.hpp"
|
||||
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
@@ -28,22 +29,6 @@ namespace ams::erpt::srv {
|
||||
static std::optional<os::Tick> s_awake_time;
|
||||
static std::optional<os::Tick> s_power_on_time;
|
||||
static std::optional<time::SteadyClockTimePoint> s_initial_launch_settings_completion_time;
|
||||
private:
|
||||
const ReportType type;
|
||||
const ContextEntry * const ctx;
|
||||
const u8 * const data;
|
||||
const u32 data_size;
|
||||
const ReportMetaData * const meta;
|
||||
const AttachmentId * const attachments;
|
||||
const u32 num_attachments;
|
||||
char identifier_str[0x40];
|
||||
time::PosixTime timestamp_user;
|
||||
time::PosixTime timestamp_network;
|
||||
os::Tick occurrence_tick;
|
||||
s64 steady_clock_internal_offset_seconds;
|
||||
ReportId report_id;
|
||||
Result ctx_result;
|
||||
time::SteadyClockTimePoint steady_clock_current_timepoint;
|
||||
public:
|
||||
static void ClearApplicationLaunchTime() { s_application_launch_time = std::nullopt; }
|
||||
static void ClearInitialLaunchSettingsCompletionTime() { s_initial_launch_settings_completion_time = std::nullopt; }
|
||||
@@ -65,20 +50,16 @@ namespace ams::erpt::srv {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
static Result RegisterRunningApplet(ncm::ProgramId program_id);
|
||||
static Result UnregisterRunningApplet(ncm::ProgramId program_id);
|
||||
static Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration);
|
||||
|
||||
static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; }
|
||||
private:
|
||||
Result ValidateReportContext();
|
||||
Result CollectUniqueReportFields();
|
||||
Result SubmitReportDefaults();
|
||||
Result SubmitReportContexts();
|
||||
Result LinkAttachments();
|
||||
Result CreateReportFile();
|
||||
void SaveSyslogReportIfRequired();
|
||||
void SaveSyslogReport();
|
||||
static Result SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const time::PosixTime &user_timestamp, const time::PosixTime &network_timestamp);
|
||||
public:
|
||||
Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, Result ctx_result);
|
||||
|
||||
Result CreateReport();
|
||||
static Result CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments);
|
||||
static Result CreateReport(ReportType type, Result ctx_result, std::unique_ptr<ContextRecord> record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user