Compare commits
53 Commits
1.4.0
...
1.5.0-prer
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
356d89244f | ||
|
|
1ce3611695 | ||
|
|
1ab8b23444 | ||
|
|
06b4738d54 | ||
|
|
b92c614347 | ||
|
|
99175c1149 | ||
|
|
8876eedfb0 | ||
|
|
bbcee8c77c | ||
|
|
f7915c2c05 | ||
|
|
668cabd4a2 | ||
|
|
d64ab354ad | ||
|
|
59b518783d | ||
|
|
6462101b6f | ||
|
|
0ea5dbcfbb | ||
|
|
bb6446aada | ||
|
|
e46e7e0eb1 | ||
|
|
cb89c66bd8 | ||
|
|
e85a512cf4 | ||
|
|
7d9fea01c3 | ||
|
|
406320f6ec | ||
|
|
aad2be0a01 | ||
|
|
25383db524 | ||
|
|
48f4c526f3 | ||
|
|
1279d236f3 | ||
|
|
008eb974d4 | ||
|
|
035cebef9d | ||
|
|
8db22967bf | ||
|
|
db510f96c3 | ||
|
|
6e2dd791b2 | ||
|
|
bff61c68ab | ||
|
|
ca7734ffaf | ||
|
|
ed22f802ee | ||
|
|
8ffc177b44 | ||
|
|
c058376b3b | ||
|
|
d5ebf13094 | ||
|
|
695c125721 | ||
|
|
e7e3e7b374 | ||
|
|
3a5f406c5f | ||
|
|
8176f085f1 | ||
|
|
7a69f2f062 | ||
|
|
32b590e7ab | ||
|
|
590f22933d | ||
|
|
0dd071b279 | ||
|
|
f1132fbf5a | ||
|
|
9cd57b6c61 | ||
|
|
bf66e40a7b | ||
|
|
77cc53227a | ||
|
|
59a295db79 | ||
|
|
e4b9930bf3 | ||
|
|
04c9004e05 | ||
|
|
1f8798ace7 | ||
|
|
ff7a80e592 | ||
|
|
e3ace4be15 |
@@ -38,7 +38,7 @@ In no particular order, we credit the following for their invaluable contributio
|
||||
|
||||
* __switchbrew__ for the [libnx](https://github.com/switchbrew/libnx) project and the extensive [documentation, research and tool development](http://switchbrew.org) pertaining to the Nintendo Switch.
|
||||
* __devkitPro__ for the [devkitA64](https://devkitpro.org/) toolchain and libnx support.
|
||||
* __ReSwitched Team__ for additional [documentation, research and tool development](https://reswitched.team/) pertaining to the Nintendo Switch.
|
||||
* __ReSwitched Team__ for additional [documentation, research and tool development](https://reswitched.github.io/) pertaining to the Nintendo Switch.
|
||||
* __ChaN__ for the [FatFs](http://elm-chan.org/fsw/ff/00index_e.html) module.
|
||||
* __Marcus Geelnard__ for the [bcl-1.2.0](https://sourceforge.net/projects/bcl/files/bcl/bcl-1.2.0) library.
|
||||
* __naehrwert__ and __st4rk__ for the original [hekate](https://github.com/nwert/hekate) project and its hwinit code base.
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
# Changelog
|
||||
## 1.5.0
|
||||
+ Support was added for 16.0.0
|
||||
+ `mesosphère` was updated to reflect the latest official kernel behavior.
|
||||
+ `ncm` was updated to reflect the latest official behavior.
|
||||
+ Many FS apis were updated under the hood to reflect the latest official behavior.
|
||||
+ **Please Note**: 16.0.0 made breaking changes to a number of system APIs, including in FS/NCM/Shared Font commands that some homebrew programs may use.
|
||||
+ These programs may encounter strange errors, and may need to be recompiled with a libnx updated to support 16.0.0's changes to function properly.
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ An issue was fixed that could cause GPIO outputs to be misconfigured under certain circumstances.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.4.1
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ `dmnt` cheat toggle files are no longer ignored when they are missing a trailing newline.
|
||||
+ The mechanism for automatically cleaning up `erpt_reports` added in 1.3.0 was fixed.
|
||||
+ This was actually just very fundamentally broken and has never worked, but it is verified working now.
|
||||
+ Minor fixes were made in `mesosphère` to match official kernel behavior (spin lock assembly was corrected, wrong result on failure in in GetProcessId was corrected).
|
||||
+ A missing call to GetSdStatus when initializing SD cards at non uhs-i mode was added in the sdmmc driver.
|
||||
+ `ams.mitm`'s memory usage was increased by 16 MB, to prevent crashing when building romfs for games with obscene file counts.
|
||||
+ To quote the changelog for 1.2.3: "Animal Crossing's 2.0.0 update contains >99000 files [...] It's really hard to imagine any game being worse than Animal Crossing".
|
||||
+ As it turns out, Fire Emblem: Engage has ~186000 files, and is approximately twice as bad as animal crossing.
|
||||
+ The additional memory here is taken from the applet pool; no issues are expected to arise from this, but please report anything you may run into.
|
||||
+ As usual, if you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
|
||||
+ I am jinxing myself by saying this, but it's really hard to imagine any game being worse than Fire Emblem: Engage, but if it happens again I will drop everything to fix it as usual.
|
||||
## 1.4.0
|
||||
+ Support was added for 15.0.0.
|
||||
+ `mesosphère` was updated to reflect the latest official kernel behavior.
|
||||
+ `ncm` was updated to reflect the latest official kernel behavior.
|
||||
+ `ncm` was updated to reflect the latest official behavior.
|
||||
+ A number of minor issues were fixed and improvements were made, including:
|
||||
+ The capacity limit on registered add-on contents was fixed in NCM to reflect the increase that occurred in 12.0.0.
|
||||
+ An off-by-one was fixed in mesosphere when computing the new value for an address arbiter signaled with ModifyByWaitingCountIfEqual.
|
||||
|
||||
4
emummc/.gitrepo
vendored
4
emummc/.gitrepo
vendored
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emummc
|
||||
branch = develop
|
||||
commit = 4714b2df9eaf68fb85516b35f7f4265ab0413825
|
||||
parent = 99f6a96845b6097d50f5059eea9245d27876f26a
|
||||
commit = bba1f1fb65f75721caf6d023a35d75d3c9aafd0f
|
||||
parent = 1ab8b234447864503e21c600681219a96962e6c1
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
2
emummc/README.md
vendored
2
emummc/README.md
vendored
@@ -2,7 +2,7 @@
|
||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||
|
||||
### Supported Horizon Versions
|
||||
**1.0.0 - 15.0.0**
|
||||
**1.0.0 - 16.0.0**
|
||||
|
||||
## Features
|
||||
* Arbitrary SDMMC backend selection
|
||||
|
||||
8
emummc/source/FS/FS_offsets.c
vendored
8
emummc/source/FS/FS_offsets.c
vendored
@@ -63,6 +63,8 @@
|
||||
#include "offsets/1400_exfat.h"
|
||||
#include "offsets/1500.h"
|
||||
#include "offsets/1500_exfat.h"
|
||||
#include "offsets/1600.h"
|
||||
#include "offsets/1600_exfat.h"
|
||||
#include "../utils/fatal.h"
|
||||
|
||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
||||
@@ -137,6 +139,8 @@ DEFINE_OFFSET_STRUCT(_1400);
|
||||
DEFINE_OFFSET_STRUCT(_1400_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1500);
|
||||
DEFINE_OFFSET_STRUCT(_1500_EXFAT);
|
||||
DEFINE_OFFSET_STRUCT(_1600);
|
||||
DEFINE_OFFSET_STRUCT(_1600_EXFAT);
|
||||
|
||||
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
switch (version) {
|
||||
@@ -234,6 +238,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1500));
|
||||
case FS_VER_15_0_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1500_EXFAT));
|
||||
case FS_VER_16_0_0:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1600));
|
||||
case FS_VER_16_0_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1600_EXFAT));
|
||||
default:
|
||||
fatal_abort(Fatal_UnknownVersion);
|
||||
}
|
||||
|
||||
3
emummc/source/FS/FS_versions.h
vendored
3
emummc/source/FS/FS_versions.h
vendored
@@ -92,6 +92,9 @@ enum FS_VER
|
||||
FS_VER_15_0_0,
|
||||
FS_VER_15_0_0_EXFAT,
|
||||
|
||||
FS_VER_16_0_0,
|
||||
FS_VER_16_0_0_EXFAT,
|
||||
|
||||
FS_VER_MAX,
|
||||
};
|
||||
|
||||
|
||||
59
emummc/source/FS/offsets/1600.h
vendored
Normal file
59
emummc/source/FS/offsets/1600.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_1600_H__
|
||||
#define __FS_1600_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1600_SDMMC_ACCESSOR_GC 0x1862A0
|
||||
#define FS_OFFSET_1600_SDMMC_ACCESSOR_SD 0x187F20
|
||||
#define FS_OFFSET_1600_SDMMC_ACCESSOR_NAND 0x186760
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1600_SDMMC_WRAPPER_READ 0x1821F0
|
||||
#define FS_OFFSET_1600_SDMMC_WRAPPER_WRITE 0x182250
|
||||
#define FS_OFFSET_1600_RTLD 0x269B0
|
||||
#define FS_OFFSET_1600_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1600_CLKRST_SET_MIN_V_CLK_RATE 0x1A2D30
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1600_LOCK_MUTEX 0x17B730
|
||||
#define FS_OFFSET_1600_UNLOCK_MUTEX 0x17B780
|
||||
|
||||
#define FS_OFFSET_1600_SDMMC_WRAPPER_CONTROLLER_OPEN 0x1821B0
|
||||
#define FS_OFFSET_1600_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1821D0
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1600_SD_MUTEX 0xFFB3F0
|
||||
#define FS_OFFSET_1600_NAND_MUTEX 0xFF6B58
|
||||
#define FS_OFFSET_1600_ACTIVE_PARTITION 0xFF6B98
|
||||
#define FS_OFFSET_1600_SDMMC_DAS_HANDLE 0xFDC8B0
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1600_SD_DAS_INIT 0x258D4
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1600_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00063B48, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00070D6C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0007790C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0008A754, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1600_H__
|
||||
59
emummc/source/FS/offsets/1600_exfat.h
vendored
Normal file
59
emummc/source/FS/offsets/1600_exfat.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __FS_1600_EXFAT_H__
|
||||
#define __FS_1600_EXFAT_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_ACCESSOR_GC 0x190F80
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_ACCESSOR_SD 0x192C00
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_ACCESSOR_NAND 0x191440
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_WRAPPER_READ 0x18CED0
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_WRAPPER_WRITE 0x18CF30
|
||||
#define FS_OFFSET_1600_EXFAT_RTLD 0x269B0
|
||||
#define FS_OFFSET_1600_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1600_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1ADA10
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1600_EXFAT_LOCK_MUTEX 0x186410
|
||||
#define FS_OFFSET_1600_EXFAT_UNLOCK_MUTEX 0x186460
|
||||
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x18CE90
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x18CEB0
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1600_EXFAT_SD_MUTEX 0x100D3F0
|
||||
#define FS_OFFSET_1600_EXFAT_NAND_MUTEX 0x1008B58
|
||||
#define FS_OFFSET_1600_EXFAT_ACTIVE_PARTITION 0x1008B98
|
||||
#define FS_OFFSET_1600_EXFAT_SDMMC_DAS_HANDLE 0xFE98B0
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1600_EXFAT_SD_DAS_INIT 0x258D4
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1600_EXFAT_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00063B48, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00070D6C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0007790C, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x0008A754, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1600_EXFAT_H__
|
||||
@@ -85,10 +85,10 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
/* We can get away with only including latest because exosphere supports newer-than-expected master key in engine. */
|
||||
/* TODO: Update on next change of keys. */
|
||||
/* Mariko Development Master Kek Source. */
|
||||
.byte 0x18, 0xA5, 0x6F, 0xEF, 0x72, 0x11, 0x62, 0xC5, 0x1A, 0x14, 0xF1, 0x8C, 0x21, 0x83, 0x27, 0xB7
|
||||
.byte 0x3A, 0x9C, 0xF0, 0x39, 0x70, 0x23, 0xF6, 0xAF, 0x71, 0x44, 0x60, 0xF4, 0x6D, 0xED, 0xA1, 0xD6
|
||||
|
||||
/* Mariko Production Master Kek Source. */
|
||||
.byte 0xEC, 0x61, 0xBC, 0x82, 0x1E, 0x0F, 0x5A, 0xC3, 0x2B, 0x64, 0x3F, 0x9D, 0xD6, 0x19, 0x22, 0x2D
|
||||
.byte 0xA5, 0xEC, 0x16, 0x39, 0x1A, 0x30, 0x16, 0x08, 0x2E, 0xCF, 0x09, 0x6F, 0x5E, 0x7C, 0xEE, 0xA9
|
||||
|
||||
/* Development Master Key Vectors. */
|
||||
.byte 0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE /* Zeroes encrypted with Master Key 00. */
|
||||
@@ -106,6 +106,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0x8A, 0xCE, 0xC4, 0x7F, 0xBE, 0x08, 0x61, 0x88, 0xD3, 0x73, 0x64, 0x51, 0xE2, 0xB6, 0x53, 0x15 /* Master key 0B encrypted with Master key 0C. */
|
||||
.byte 0x08, 0xE0, 0xF4, 0xBE, 0xAA, 0x6E, 0x5A, 0xC3, 0xA6, 0xBC, 0xFE, 0xB9, 0xE2, 0xA3, 0x24, 0x12 /* Master key 0C encrypted with Master key 0D. */
|
||||
.byte 0xD6, 0x80, 0x98, 0xC0, 0xFA, 0xC7, 0x13, 0xCB, 0x93, 0xD2, 0x0B, 0x82, 0x4C, 0xA1, 0x7B, 0x8D /* Master key 0D encrypted with Master key 0E. */
|
||||
.byte 0x78, 0x66, 0x19, 0xBD, 0x86, 0xE7, 0xC1, 0x09, 0x9B, 0x6F, 0x92, 0xB2, 0x58, 0x7D, 0xCF, 0x26 /* Master key 0E encrypted with Master key 0F. */
|
||||
|
||||
/* Production Master Key Vectors. */
|
||||
.byte 0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D /* Zeroes encrypted with Master Key 00. */
|
||||
@@ -123,6 +124,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xA3, 0x24, 0x65, 0x75, 0xEA, 0xCC, 0x6E, 0x8D, 0xFB, 0x5A, 0x16, 0x50, 0x74, 0xD2, 0x15, 0x06 /* Master key 0B encrypted with Master key 0C. */
|
||||
.byte 0x83, 0x67, 0xAF, 0x01, 0xCF, 0x93, 0xA1, 0xAB, 0x80, 0x45, 0xF7, 0x3F, 0x72, 0xFD, 0x3B, 0x38 /* Master key 0C encrypted with Master key 0D. */
|
||||
.byte 0xB1, 0x81, 0xA6, 0x0D, 0x72, 0xC7, 0xEE, 0x15, 0x21, 0xF3, 0xC0, 0xB5, 0x6B, 0x61, 0x6D, 0xE7 /* Master key 0D encrypted with Master key 0E. */
|
||||
.byte 0xAF, 0x11, 0x4C, 0x67, 0x17, 0x7A, 0x52, 0x43, 0xF7, 0x70, 0x2F, 0xC7, 0xEF, 0x81, 0x72, 0x16 /* Master key 0E encrypted with Master key 0F. */
|
||||
|
||||
/* Device Master Key Source Sources. */
|
||||
.byte 0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D /* 4.0.0 Device Master Key Source Source. */
|
||||
@@ -137,6 +139,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xE4, 0xF3, 0x45, 0x6F, 0x18, 0xA1, 0x89, 0xF8, 0xDA, 0x4C, 0x64, 0x75, 0x68, 0xE6, 0xBD, 0x4F /* 13.0.0 Device Master Key Source Source. */
|
||||
.byte 0x5B, 0x94, 0x63, 0xF7, 0xAD, 0x96, 0x1B, 0xA6, 0x23, 0x30, 0x06, 0x4D, 0x01, 0xE4, 0xCE, 0x1D /* 14.0.0 Device Master Key Source Source. */
|
||||
.byte 0x5E, 0xC9, 0xC5, 0x0A, 0xD0, 0x5F, 0x8B, 0x7B, 0xA7, 0x39, 0xEA, 0xBC, 0x60, 0x0F, 0x74, 0xE6 /* 15.0.0 Device Master Key Source Source. */
|
||||
.byte 0xEA, 0x90, 0x6E, 0xA8, 0xAE, 0x92, 0x99, 0x64, 0x36, 0xC1, 0xF3, 0x1C, 0xC6, 0x32, 0x83, 0x8C /* 16.0.0 Device Master Key Source Source. */
|
||||
|
||||
/* Development Device Master Kek Sources. */
|
||||
.byte 0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34 /* 4.0.0 Device Master Kek Source. */
|
||||
@@ -151,6 +154,7 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0x20, 0x20, 0xAA, 0xFB, 0x89, 0xC2, 0xF0, 0x70, 0xB5, 0xE0, 0xA3, 0x11, 0x8A, 0x29, 0x8D, 0x0F /* 13.0.0 Device Master Kek Source. */
|
||||
.byte 0xCE, 0x14, 0x74, 0x66, 0x98, 0xA8, 0x6D, 0x7D, 0xBD, 0x54, 0x91, 0x68, 0x5F, 0x1D, 0x0E, 0xEA /* 14.0.0 Device Master Kek Source. */
|
||||
.byte 0xAE, 0x05, 0x48, 0x65, 0xAB, 0x17, 0x9D, 0x3D, 0x51, 0xB7, 0x56, 0xBD, 0x9B, 0x0B, 0x5B, 0x6E /* 15.0.0 Device Master Kek Source. */
|
||||
.byte 0xFF, 0xF6, 0x4B, 0x0F, 0xFF, 0x0D, 0xC0, 0x4F, 0x56, 0x8A, 0x40, 0x74, 0x67, 0xC5, 0xFE, 0x9F /* 16.0.0 Device Master Kek Source. */
|
||||
|
||||
/* Production Device Master Kek Sources. */
|
||||
.byte 0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D /* 4.0.0 Device Master Kek Source. */
|
||||
@@ -165,3 +169,4 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0x77, 0x52, 0x92, 0xF0, 0xAA, 0xE3, 0xFB, 0xE0, 0x60, 0x16, 0xB3, 0x78, 0x68, 0x53, 0xF7, 0xA8 /* 13.0.0 Device Master Kek Source. */
|
||||
.byte 0x67, 0xD5, 0xD6, 0x0C, 0x08, 0xF5, 0xA3, 0x11, 0xBD, 0x6D, 0x5A, 0xEB, 0x96, 0x24, 0xB0, 0xD2 /* 14.0.0 Device Master Kek Source. */
|
||||
.byte 0x7C, 0x30, 0xED, 0x8B, 0x39, 0x25, 0x2C, 0x08, 0x8F, 0x48, 0xDC, 0x28, 0xE6, 0x1A, 0x6B, 0x49 /* 15.0.0 Device Master Kek Source. */
|
||||
.byte 0xF0, 0xF3, 0xFF, 0x52, 0x75, 0x2F, 0xBA, 0x4D, 0x09, 0x72, 0x30, 0x89, 0xA9, 0xDF, 0xFE, 0x1F /* 16.0.0 Device Master Kek Source. */
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ams::secmon::boot {
|
||||
}
|
||||
|
||||
/* Check that the key generation is one that we can use. */
|
||||
static_assert(pkg1::KeyGeneration_Count == 15);
|
||||
static_assert(pkg1::KeyGeneration_Count == 16);
|
||||
if (key_generation >= pkg1::KeyGeneration_Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,17 +23,17 @@ namespace ams::nxboot {
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 MarikoMasterKekSource[se::AesBlockSize] = {
|
||||
/* TODO: Update on next change of keys. */
|
||||
0xEC, 0x61, 0xBC, 0x82, 0x1E, 0x0F, 0x5A, 0xC3, 0x2B, 0x64, 0x3F, 0x9D, 0xD6, 0x19, 0x22, 0x2D
|
||||
0xA5, 0xEC, 0x16, 0x39, 0x1A, 0x30, 0x16, 0x08, 0x2E, 0xCF, 0x09, 0x6F, 0x5E, 0x7C, 0xEE, 0xA9
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 MarikoMasterKekSourceDev[se::AesBlockSize] = {
|
||||
/* TODO: Update on next change of keys. */
|
||||
0x18, 0xA5, 0x6F, 0xEF, 0x72, 0x11, 0x62, 0xC5, 0x1A, 0x14, 0xF1, 0x8C, 0x21, 0x83, 0x27, 0xB7
|
||||
0x3A, 0x9C, 0xF0, 0x39, 0x70, 0x23, 0xF6, 0xAF, 0x71, 0x44, 0x60, 0xF4, 0x6D, 0xED, 0xA1, 0xD6
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 EristaMasterKekSource[se::AesBlockSize] = {
|
||||
/* TODO: Update on next change of keys. */
|
||||
0x6E, 0x77, 0x86, 0xAC, 0x83, 0x0A, 0x8D, 0x3E, 0x7D, 0xB7, 0x66, 0xA0, 0x22, 0xB7, 0x6E, 0x67
|
||||
0x99, 0x22, 0x09, 0x57, 0xA7, 0xF9, 0x5E, 0x94, 0xFE, 0x78, 0x7F, 0x41, 0xD6, 0xE7, 0x56, 0xE6
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 KeyblobKeySource[se::AesBlockSize] = {
|
||||
@@ -69,6 +69,7 @@ namespace ams::nxboot {
|
||||
{ 0xE4, 0xF3, 0x45, 0x6F, 0x18, 0xA1, 0x89, 0xF8, 0xDA, 0x4C, 0x64, 0x75, 0x68, 0xE6, 0xBD, 0x4F }, /* 13.0.0 Device Master Key Source Source. */
|
||||
{ 0x5B, 0x94, 0x63, 0xF7, 0xAD, 0x96, 0x1B, 0xA6, 0x23, 0x30, 0x06, 0x4D, 0x01, 0xE4, 0xCE, 0x1D }, /* 14.0.0 Device Master Key Source Source. */
|
||||
{ 0x5E, 0xC9, 0xC5, 0x0A, 0xD0, 0x5F, 0x8B, 0x7B, 0xA7, 0x39, 0xEA, 0xBC, 0x60, 0x0F, 0x74, 0xE6 }, /* 15.0.0 Device Master Key Source Source. */
|
||||
{ 0xEA, 0x90, 0x6E, 0xA8, 0xAE, 0x92, 0x99, 0x64, 0x36, 0xC1, 0xF3, 0x1C, 0xC6, 0x32, 0x83, 0x8C }, /* 16.0.0 Device Master Key Source Source. */
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 DeviceMasterKekSources[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
|
||||
@@ -84,6 +85,7 @@ namespace ams::nxboot {
|
||||
{ 0x77, 0x52, 0x92, 0xF0, 0xAA, 0xE3, 0xFB, 0xE0, 0x60, 0x16, 0xB3, 0x78, 0x68, 0x53, 0xF7, 0xA8 }, /* 13.0.0 Device Master Kek Source. */
|
||||
{ 0x67, 0xD5, 0xD6, 0x0C, 0x08, 0xF5, 0xA3, 0x11, 0xBD, 0x6D, 0x5A, 0xEB, 0x96, 0x24, 0xB0, 0xD2 }, /* 14.0.0 Device Master Kek Source. */
|
||||
{ 0x7C, 0x30, 0xED, 0x8B, 0x39, 0x25, 0x2C, 0x08, 0x8F, 0x48, 0xDC, 0x28, 0xE6, 0x1A, 0x6B, 0x49 }, /* 15.0.0 Device Master Kek Source. */
|
||||
{ 0xF0, 0xF3, 0xFF, 0x52, 0x75, 0x2F, 0xBA, 0x4D, 0x09, 0x72, 0x30, 0x89, 0xA9, 0xDF, 0xFE, 0x1F }, /* 16.0.0 Device Master Kek Source. */
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 DeviceMasterKekSourcesDev[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
|
||||
@@ -99,6 +101,7 @@ namespace ams::nxboot {
|
||||
{ 0x20, 0x20, 0xAA, 0xFB, 0x89, 0xC2, 0xF0, 0x70, 0xB5, 0xE0, 0xA3, 0x11, 0x8A, 0x29, 0x8D, 0x0F }, /* 13.0.0 Device Master Kek Source. */
|
||||
{ 0xCE, 0x14, 0x74, 0x66, 0x98, 0xA8, 0x6D, 0x7D, 0xBD, 0x54, 0x91, 0x68, 0x5F, 0x1D, 0x0E, 0xEA }, /* 14.0.0 Device Master Kek Source. */
|
||||
{ 0xAE, 0x05, 0x48, 0x65, 0xAB, 0x17, 0x9D, 0x3D, 0x51, 0xB7, 0x56, 0xBD, 0x9B, 0x0B, 0x5B, 0x6E }, /* 15.0.0 Device Master Kek Source. */
|
||||
{ 0xFF, 0xF6, 0x4B, 0x0F, 0xFF, 0x0D, 0xC0, 0x4F, 0x56, 0x8A, 0x40, 0x74, 0x67, 0xC5, 0xFE, 0x9F }, /* 16.0.0 Device Master Kek Source. */
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 MasterKeySources[pkg1::KeyGeneration_Count][se::AesBlockSize] = {
|
||||
@@ -117,6 +120,7 @@ namespace ams::nxboot {
|
||||
{ 0xA3, 0x24, 0x65, 0x75, 0xEA, 0xCC, 0x6E, 0x8D, 0xFB, 0x5A, 0x16, 0x50, 0x74, 0xD2, 0x15, 0x06 }, /* Master key 0B encrypted with Master key 0C. */
|
||||
{ 0x83, 0x67, 0xAF, 0x01, 0xCF, 0x93, 0xA1, 0xAB, 0x80, 0x45, 0xF7, 0x3F, 0x72, 0xFD, 0x3B, 0x38 }, /* Master key 0C encrypted with Master key 0D. */
|
||||
{ 0xB1, 0x81, 0xA6, 0x0D, 0x72, 0xC7, 0xEE, 0x15, 0x21, 0xF3, 0xC0, 0xB5, 0x6B, 0x61, 0x6D, 0xE7 }, /* Master key 0D encrypted with Master key 0E. */
|
||||
{ 0xAF, 0x11, 0x4C, 0x67, 0x17, 0x7A, 0x52, 0x43, 0xF7, 0x70, 0x2F, 0xC7, 0xEF, 0x81, 0x72, 0x16 }, /* Master key 0E encrypted with Master key 0F. */
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 MasterKeySourcesDev[pkg1::KeyGeneration_Count][se::AesBlockSize] = {
|
||||
@@ -135,6 +139,7 @@ namespace ams::nxboot {
|
||||
{ 0x8A, 0xCE, 0xC4, 0x7F, 0xBE, 0x08, 0x61, 0x88, 0xD3, 0x73, 0x64, 0x51, 0xE2, 0xB6, 0x53, 0x15 }, /* Master key 0B encrypted with Master key 0C. */
|
||||
{ 0x08, 0xE0, 0xF4, 0xBE, 0xAA, 0x6E, 0x5A, 0xC3, 0xA6, 0xBC, 0xFE, 0xB9, 0xE2, 0xA3, 0x24, 0x12 }, /* Master key 0C encrypted with Master key 0D. */
|
||||
{ 0xD6, 0x80, 0x98, 0xC0, 0xFA, 0xC7, 0x13, 0xCB, 0x93, 0xD2, 0x0B, 0x82, 0x4C, 0xA1, 0x7B, 0x8D }, /* Master key 0D encrypted with Master key 0E. */
|
||||
{ 0x78, 0x66, 0x19, 0xBD, 0x86, 0xE7, 0xC1, 0x09, 0x9B, 0x6F, 0x92, 0xB2, 0x58, 0x7D, 0xCF, 0x26 }, /* Master key 0E encrypted with Master key 0F. */
|
||||
};
|
||||
|
||||
alignas(se::AesBlockSize) constinit u8 MasterKeys[pkg1::OldMasterKeyCount][se::AesBlockSize] = {};
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace ams::nxboot {
|
||||
}
|
||||
|
||||
/* Check that the key generation is one that we can use. */
|
||||
static_assert(pkg1::KeyGeneration_Count == 15);
|
||||
static_assert(pkg1::KeyGeneration_Count == 16);
|
||||
if (key_generation >= pkg1::KeyGeneration_Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -255,6 +255,8 @@ namespace ams::nxboot {
|
||||
return ams::TargetFirmware_14_0_0;
|
||||
} else if (std::memcmp(package1 + 0x10, "20220801", 8) == 0) {
|
||||
return ams::TargetFirmware_15_0_0;
|
||||
} else if (std::memcmp(package1 + 0x10, "20230111", 8) == 0) {
|
||||
return ams::TargetFirmware_16_0_0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -159,6 +159,9 @@ namespace ams::nxboot {
|
||||
FsVersion_15_0_0,
|
||||
FsVersion_15_0_0_Exfat,
|
||||
|
||||
FsVersion_16_0_0,
|
||||
FsVersion_16_0_0_Exfat,
|
||||
|
||||
FsVersion_Count,
|
||||
};
|
||||
|
||||
@@ -231,9 +234,11 @@ namespace ams::nxboot {
|
||||
{ 0x88, 0x7A, 0xC1, 0x50, 0x80, 0x6C, 0x75, 0xCC }, /* FsVersion_14_0_0 */
|
||||
{ 0xD4, 0x88, 0xD1, 0xF2, 0x92, 0x17, 0x35, 0x5C }, /* FsVersion_14_0_0_Exfat */
|
||||
|
||||
|
||||
{ 0xD0, 0xD4, 0x49, 0x18, 0x14, 0xB5, 0x62, 0xAF }, /* FsVersion_15_0_0 */
|
||||
{ 0x34, 0xC0, 0xD9, 0xED, 0x6A, 0xD1, 0x87, 0x3D }, /* FsVersion_15_0_0_Exfat */
|
||||
|
||||
{ 0x56, 0xE8, 0x56, 0x56, 0x6C, 0x38, 0xD8, 0xBE }, /* FsVersion_16_0_0 */
|
||||
{ 0xCF, 0xAB, 0x45, 0x0C, 0x2C, 0x53, 0x9D, 0xA9 }, /* FsVersion_16_0_0_Exfat */
|
||||
};
|
||||
|
||||
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
|
||||
@@ -643,6 +648,14 @@ namespace ams::nxboot {
|
||||
AddPatch(fs_meta, 0x18F1E9, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x169D74, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
case FsVersion_16_0_0:
|
||||
AddPatch(fs_meta, 0x1866D9, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x160C70, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
case FsVersion_16_0_0_Exfat:
|
||||
AddPatch(fs_meta, 0x1913B9, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x16B950, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = b2232894f31953511d33bc665f68216badc3bb07
|
||||
parent = 8ce4f1961580a09381e486cc51c160a00fa86b88
|
||||
commit = b7711b8fbcec5013e1738218267d69b2cb44f85e
|
||||
parent = 590f22933db38f089ab2224f7fd86658a9533e8d
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ams::pkg1 {
|
||||
enum MemoryArrange {
|
||||
MemoryArrange_Normal = 1,
|
||||
MemoryArrange_AppletDev = 2,
|
||||
MemoryArrange_SystemDev = 2,
|
||||
MemoryArrange_SystemDev = 3,
|
||||
};
|
||||
|
||||
enum MemoryMode {
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace ams::pkg1 {
|
||||
KeyGeneration_13_0_0 = 0x0C,
|
||||
KeyGeneration_14_0_0 = 0x0D,
|
||||
KeyGeneration_15_0_0 = 0x0E,
|
||||
KeyGeneration_16_0_0 = 0x0F,
|
||||
|
||||
KeyGeneration_Count,
|
||||
|
||||
|
||||
@@ -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 0x16 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0x12;
|
||||
constexpr inline int MinimumValidDataVersion = 0; /* We allow older package2 to load; this value is currently 0x17 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0x13;
|
||||
|
||||
struct Package2Meta {
|
||||
using Magic = util::FourCC<'P','K','2','1'>;
|
||||
|
||||
@@ -177,6 +177,7 @@ namespace ams::fuse {
|
||||
}
|
||||
|
||||
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
|
||||
TargetFirmware_16_0_0,
|
||||
TargetFirmware_15_0_0,
|
||||
TargetFirmware_13_2_1,
|
||||
TargetFirmware_12_0_2,
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace ams::kern::init {
|
||||
u64 sp;
|
||||
u64 entrypoint;
|
||||
u64 argument;
|
||||
u64 setup_function;
|
||||
u64 exception_stack;
|
||||
};
|
||||
static_assert(alignof(KInitArguments) == util::CeilingPowerOfTwo(INIT_ARGUMENTS_SIZE));
|
||||
static_assert(sizeof(KInitArguments) == std::max(INIT_ARGUMENTS_SIZE, util::CeilingPowerOfTwo(INIT_ARGUMENTS_SIZE)));
|
||||
@@ -45,7 +43,5 @@ namespace ams::kern::init {
|
||||
static_assert(AMS_OFFSETOF(KInitArguments, sp) == INIT_ARGUMENTS_SP);
|
||||
static_assert(AMS_OFFSETOF(KInitArguments, entrypoint) == INIT_ARGUMENTS_ENTRYPOINT);
|
||||
static_assert(AMS_OFFSETOF(KInitArguments, argument) == INIT_ARGUMENTS_ARGUMENT);
|
||||
static_assert(AMS_OFFSETOF(KInitArguments, setup_function) == INIT_ARGUMENTS_SETUP_FUNCTION);
|
||||
static_assert(AMS_OFFSETOF(KInitArguments, exception_stack) == INIT_ARGUMENTS_EXCEPTION_STACK);
|
||||
|
||||
}
|
||||
@@ -23,17 +23,6 @@
|
||||
|
||||
namespace ams::kern::arch::arm64::init {
|
||||
|
||||
inline void ClearPhysicalMemory(KPhysicalAddress address, size_t size) {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, sizeof(u64)));
|
||||
|
||||
/* This Physical Address -> void * conversion is valid, because this is init page table code. */
|
||||
/* The MMU is necessarily not yet turned on, if we are creating an initial page table. */
|
||||
volatile u64 *ptr = reinterpret_cast<volatile u64 *>(GetInteger(address));
|
||||
for (size_t i = 0; i < size / sizeof(u64); ++i) {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: Nintendo uses virtual functions, rather than a concept + template. */
|
||||
template<typename T>
|
||||
concept IsInitialPageAllocator = requires (T &t, KPhysicalAddress phys_addr, size_t size) {
|
||||
@@ -41,25 +30,23 @@ namespace ams::kern::arch::arm64::init {
|
||||
{ t.Free(phys_addr, size) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template<IsInitialPageAllocator _PageAllocator>
|
||||
class KInitialPageTableTemplate {
|
||||
public:
|
||||
using PageAllocator = _PageAllocator;
|
||||
class KInitialPageTable {
|
||||
private:
|
||||
KPhysicalAddress m_l1_tables[2];
|
||||
u32 m_num_entries[2];
|
||||
public:
|
||||
KInitialPageTableTemplate(KVirtualAddress start_address, KVirtualAddress end_address, PageAllocator &allocator) {
|
||||
template<IsInitialPageAllocator PageAllocator>
|
||||
KInitialPageTable(KVirtualAddress start_address, KVirtualAddress end_address, PageAllocator &allocator) {
|
||||
/* Set tables. */
|
||||
m_l1_tables[0] = AllocateNewPageTable(allocator);
|
||||
m_l1_tables[1] = AllocateNewPageTable(allocator);
|
||||
m_l1_tables[0] = AllocateNewPageTable(allocator, 0);
|
||||
m_l1_tables[1] = AllocateNewPageTable(allocator, 0);
|
||||
|
||||
/* Set counts. */
|
||||
m_num_entries[0] = MaxPageTableEntries;
|
||||
m_num_entries[1] = ((end_address / L1BlockSize) & (MaxPageTableEntries - 1)) - ((start_address / L1BlockSize) & (MaxPageTableEntries - 1)) + 1;
|
||||
}
|
||||
|
||||
KInitialPageTableTemplate() {
|
||||
KInitialPageTable() {
|
||||
/* Set tables. */
|
||||
m_l1_tables[0] = util::AlignDown(cpu::GetTtbr0El1(), PageSize);
|
||||
m_l1_tables[1] = util::AlignDown(cpu::GetTtbr1El1(), PageSize);
|
||||
@@ -82,30 +69,35 @@ namespace ams::kern::arch::arm64::init {
|
||||
return GetInteger(m_l1_tables[1]);
|
||||
}
|
||||
private:
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KVirtualAddress address) const {
|
||||
constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KVirtualAddress address, u64 phys_to_virt_offset = 0) const {
|
||||
const size_t index = (GetInteger(address) >> (BITSIZEOF(address) - 1)) & 1;
|
||||
L1PageTableEntry *l1_table = reinterpret_cast<L1PageTableEntry *>(GetInteger(m_l1_tables[index]));
|
||||
L1PageTableEntry *l1_table = reinterpret_cast<L1PageTableEntry *>(GetInteger(m_l1_tables[index]) + phys_to_virt_offset);
|
||||
return l1_table + ((GetInteger(address) / L1BlockSize) & (m_num_entries[index] - 1));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address) {
|
||||
L2PageTableEntry *l2_table = reinterpret_cast<L2PageTableEntry *>(GetInteger(entry->GetTable()));
|
||||
static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address, u64 phys_to_virt_offset = 0) {
|
||||
L2PageTableEntry *l2_table = reinterpret_cast<L2PageTableEntry *>(GetInteger(entry->GetTable()) + phys_to_virt_offset);
|
||||
return l2_table + ((GetInteger(address) / L2BlockSize) & (MaxPageTableEntries - 1));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address) {
|
||||
L3PageTableEntry *l3_table = reinterpret_cast<L3PageTableEntry *>(GetInteger(entry->GetTable()));
|
||||
static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address, u64 phys_to_virt_offset = 0) {
|
||||
L3PageTableEntry *l3_table = reinterpret_cast<L3PageTableEntry *>(GetInteger(entry->GetTable()) + phys_to_virt_offset);
|
||||
return l3_table + ((GetInteger(address) / L3BlockSize) & (MaxPageTableEntries - 1));
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPhysicalAddress AllocateNewPageTable(PageAllocator &allocator) {
|
||||
template<IsInitialPageAllocator PageAllocator>
|
||||
static ALWAYS_INLINE KPhysicalAddress AllocateNewPageTable(PageAllocator &allocator, u64 phys_to_virt_offset) {
|
||||
auto address = allocator.Allocate(PageSize);
|
||||
ClearNewPageTable(address);
|
||||
ClearNewPageTable(address, phys_to_virt_offset);
|
||||
return address;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) {
|
||||
ClearPhysicalMemory(address, PageSize);
|
||||
static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address, u64 phys_to_virt_offset) {
|
||||
/* Convert to a deferenceable address, and clear. */
|
||||
volatile u64 *ptr = reinterpret_cast<volatile u64 *>(GetInteger(address) + phys_to_virt_offset);
|
||||
for (size_t i = 0; i < PageSize / sizeof(u64); ++i) {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
}
|
||||
public:
|
||||
static consteval size_t GetMaximumOverheadSize(size_t size) {
|
||||
@@ -327,7 +319,8 @@ namespace ams::kern::arch::arm64::init {
|
||||
}
|
||||
}
|
||||
public:
|
||||
void NOINLINE Map(KVirtualAddress virt_addr, size_t size, KPhysicalAddress phys_addr, const PageTableEntry &attr, PageAllocator &allocator) {
|
||||
template<IsInitialPageAllocator PageAllocator>
|
||||
void NOINLINE Map(KVirtualAddress virt_addr, size_t size, KPhysicalAddress phys_addr, const PageTableEntry &attr, PageAllocator &allocator, u64 phys_to_virt_offset) {
|
||||
/* Ensure that addresses and sizes are page aligned. */
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize));
|
||||
@@ -335,7 +328,7 @@ namespace ams::kern::arch::arm64::init {
|
||||
|
||||
/* Iteratively map pages until the requested region is mapped. */
|
||||
while (size > 0) {
|
||||
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
|
||||
L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr, phys_to_virt_offset);
|
||||
|
||||
/* Can we make an L1 block? */
|
||||
if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && size >= L1BlockSize) {
|
||||
@@ -349,12 +342,12 @@ namespace ams::kern::arch::arm64::init {
|
||||
|
||||
/* If we don't already have an L2 table, we need to make a new one. */
|
||||
if (!l1_entry->IsTable()) {
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator, phys_to_virt_offset);
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
*l1_entry = L1PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||
}
|
||||
|
||||
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
|
||||
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr, phys_to_virt_offset);
|
||||
|
||||
/* Can we make a contiguous L2 block? */
|
||||
if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && size >= L2ContiguousBlockSize) {
|
||||
@@ -380,12 +373,12 @@ namespace ams::kern::arch::arm64::init {
|
||||
|
||||
/* If we don't already have an L3 table, we need to make a new one. */
|
||||
if (!l2_entry->IsTable()) {
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator);
|
||||
KPhysicalAddress new_table = AllocateNewPageTable(allocator, phys_to_virt_offset);
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
*l2_entry = L2PageTableEntry(PageTableEntry::TableTag{}, new_table, attr.IsPrivilegedExecuteNever());
|
||||
}
|
||||
|
||||
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
|
||||
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr, phys_to_virt_offset);
|
||||
|
||||
/* Can we make a contiguous L3 block? */
|
||||
if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && size >= L3ContiguousBlockSize) {
|
||||
@@ -410,6 +403,98 @@ namespace ams::kern::arch::arm64::init {
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
void UnmapTtbr0Entries(u64 phys_to_virt_offset) {
|
||||
/* Ensure data consistency before we unmap. */
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Define helper, as we only want to clear non-nGnRE pages. */
|
||||
constexpr auto ShouldUnmap = [](const PageTableEntry *entry) ALWAYS_INLINE_LAMBDA -> bool {
|
||||
return entry->GetPageAttribute() != PageTableEntry::PageAttribute_Device_nGnRE;
|
||||
};
|
||||
|
||||
/* Iterate all L1 entries. */
|
||||
L1PageTableEntry * const l1_table = reinterpret_cast<L1PageTableEntry *>(GetInteger(m_l1_tables[0]) + phys_to_virt_offset);
|
||||
for (size_t l1_index = 0; l1_index < m_num_entries[0]; l1_index++) {
|
||||
/* Get L1 entry. */
|
||||
L1PageTableEntry * const l1_entry = l1_table + l1_index;
|
||||
if (l1_entry->IsBlock()) {
|
||||
/* Unmap the L1 entry, if we should. */
|
||||
if (ShouldUnmap(l1_entry)) {
|
||||
*static_cast<PageTableEntry *>(l1_entry) = InvalidPageTableEntry;
|
||||
}
|
||||
} else if (l1_entry->IsTable()) {
|
||||
/* Get the L2 table. */
|
||||
L2PageTableEntry * const l2_table = reinterpret_cast<L2PageTableEntry *>(GetInteger(l1_entry->GetTable()) + phys_to_virt_offset);
|
||||
|
||||
/* Unmap all L2 entries, as relevant. */
|
||||
size_t remaining_l2_entries = 0;
|
||||
for (size_t l2_index = 0; l2_index < MaxPageTableEntries; ++l2_index) {
|
||||
/* Get L2 entry. */
|
||||
L2PageTableEntry * const l2_entry = l2_table + l2_index;
|
||||
if (l2_entry->IsBlock()) {
|
||||
const size_t num_to_clear = (l2_entry->IsContiguous() ? L2ContiguousBlockSize : L2BlockSize) / L2BlockSize;
|
||||
|
||||
if (ShouldUnmap(l2_entry)) {
|
||||
for (size_t i = 0; i < num_to_clear; ++i) {
|
||||
static_cast<PageTableEntry *>(l2_entry)[i] = InvalidPageTableEntry;
|
||||
}
|
||||
} else {
|
||||
remaining_l2_entries += num_to_clear;
|
||||
}
|
||||
|
||||
l2_index = l2_index + num_to_clear - 1;
|
||||
} else if (l2_entry->IsTable()) {
|
||||
/* Get the L3 table. */
|
||||
L3PageTableEntry * const l3_table = reinterpret_cast<L3PageTableEntry *>(GetInteger(l2_entry->GetTable()) + phys_to_virt_offset);
|
||||
|
||||
/* Unmap all L3 entries, as relevant. */
|
||||
size_t remaining_l3_entries = 0;
|
||||
for (size_t l3_index = 0; l3_index < MaxPageTableEntries; ++l3_index) {
|
||||
/* Get L3 entry. */
|
||||
if (L3PageTableEntry * const l3_entry = l3_table + l3_index; l3_entry->IsBlock()) {
|
||||
const size_t num_to_clear = (l3_entry->IsContiguous() ? L3ContiguousBlockSize : L3BlockSize) / L3BlockSize;
|
||||
|
||||
if (ShouldUnmap(l3_entry)) {
|
||||
for (size_t i = 0; i < num_to_clear; ++i) {
|
||||
static_cast<PageTableEntry *>(l3_entry)[i] = InvalidPageTableEntry;
|
||||
}
|
||||
} else {
|
||||
remaining_l3_entries += num_to_clear;
|
||||
}
|
||||
|
||||
l3_index = l3_index + num_to_clear - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we unmapped all L3 entries, clear the L2 entry. */
|
||||
if (remaining_l3_entries == 0) {
|
||||
*static_cast<PageTableEntry *>(l2_entry) = InvalidPageTableEntry;
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
cpu::InvalidateEntireTlb();
|
||||
} else {
|
||||
remaining_l2_entries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we unmapped all L2 entries, clear the L1 entry. */
|
||||
if (remaining_l2_entries == 0) {
|
||||
*static_cast<PageTableEntry *>(l1_entry) = InvalidPageTableEntry;
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
cpu::InvalidateEntireTlb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
cpu::DataSynchronizationBarrierInnerShareable();
|
||||
cpu::InvalidateEntireTlb();
|
||||
}
|
||||
|
||||
KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const {
|
||||
/* Get the L1 entry. */
|
||||
const L1PageTableEntry *l1_entry = this->GetL1Entry(virt_addr);
|
||||
@@ -861,6 +946,4 @@ namespace ams::kern::arch::arm64::init {
|
||||
};
|
||||
static_assert(IsInitialPageAllocator<KInitialPageAllocator>);
|
||||
|
||||
using KInitialPageTable = KInitialPageTableTemplate<KInitialPageAllocator>;
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define THREAD_KERNEL_STACK_TOP 0x280
|
||||
|
||||
/* 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 0x130
|
||||
#define THREAD_STACK_PARAMETERS_SIZE 0x140
|
||||
#define THREAD_STACK_PARAMETERS_SVC_PERMISSION 0x00
|
||||
#define THREAD_STACK_PARAMETERS_CALLER_SAVE_FPU_REGISTERS 0x18
|
||||
#define THREAD_STACK_PARAMETERS_CUR_THREAD 0x20
|
||||
@@ -34,7 +34,8 @@
|
||||
#define THREAD_STACK_PARAMETERS_EXCEPTION_FLAGS 0x2D
|
||||
#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E
|
||||
#define THREAD_STACK_PARAMETERS_RESERVED_2F 0x2F
|
||||
#define THREAD_STACK_PARAMETERS_THREAD_CONTEXT 0x30
|
||||
#define THREAD_STACK_PARAMETERS_RESERVED_30 0x30
|
||||
#define THREAD_STACK_PARAMETERS_THREAD_CONTEXT 0x40
|
||||
|
||||
#define THREAD_EXCEPTION_FLAG_BIT_INDEX_IS_CALLING_SVC (0)
|
||||
#define THREAD_EXCEPTION_FLAG_BIT_INDEX_IS_IN_EXCEPTION_HANDLER (1)
|
||||
@@ -245,7 +246,7 @@
|
||||
#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_SIZE 0x50
|
||||
#define INIT_ARGUMENTS_TTBR0 0x00
|
||||
#define INIT_ARGUMENTS_TTBR1 0x08
|
||||
#define INIT_ARGUMENTS_TCR 0x10
|
||||
@@ -256,14 +257,12 @@
|
||||
#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_RUNNABLE 0x01
|
||||
#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x10
|
||||
#define KSCHEDULER_IDLE_THREAD_STACK 0x18
|
||||
#define KSCHEDULER_PREVIOUS_THREAD 0x20
|
||||
#define KSCHEDULER_INTERRUPT_TASK_MANAGER 0x28
|
||||
#define KSCHEDULER_HIGHEST_PRIORITY_THREAD 0x18
|
||||
#define KSCHEDULER_IDLE_THREAD_STACK 0x20
|
||||
#define KSCHEDULER_PREVIOUS_THREAD 0x28
|
||||
#define KSCHEDULER_INTERRUPT_TASK_MANAGER 0x30
|
||||
|
||||
@@ -36,6 +36,9 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
#error "Unknown Board for cpu::NumCores"
|
||||
#endif
|
||||
|
||||
constexpr inline u32 El0Aarch64PsrMask = 0xF0000000;
|
||||
constexpr inline u32 El0Aarch32PsrMask = 0xFE0FFE20;
|
||||
|
||||
/* Initialization. */
|
||||
NOINLINE void InitializeInterruptThreads(s32 core_id);
|
||||
|
||||
@@ -186,6 +189,14 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
return (par >> (BITSIZEOF(par) - BITSIZEOF(u8))) == 0xFF;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void StoreDataCacheForInitArguments(const void *addr, size_t size) {
|
||||
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), DataCacheLineSize);
|
||||
for (size_t stored = 0; stored < size; stored += cpu::DataCacheLineSize) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" :: [cur]"r"(start + stored) : "memory");
|
||||
}
|
||||
DataSynchronizationBarrier();
|
||||
}
|
||||
|
||||
/* Synchronization helpers. */
|
||||
NOINLINE void SynchronizeAllCores();
|
||||
void SynchronizeCores(u64 core_mask);
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace ams::kern::arch::arm64 {
|
||||
ALWAYS_INLINE Result SeparatePagesImpl(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result SeparatePages(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
||||
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, bool flush_mapping, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
||||
static ALWAYS_INLINE void PteDataMemoryBarrier() {
|
||||
cpu::DataMemoryBarrierInnerShareableStore();
|
||||
|
||||
@@ -161,9 +161,12 @@ namespace ams::kern::arch::arm64 {
|
||||
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsGlobal() const { return this->GetBits(11, 1) == 0; }
|
||||
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
|
||||
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
|
||||
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
|
||||
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->SelectBits(10, 1)); }
|
||||
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->SelectBits(8, 2)); }
|
||||
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->SelectBits(2, 3)); }
|
||||
constexpr ALWAYS_INLINE int GetAccessFlagInteger() const { return static_cast<int>(this->GetBits(10, 1)); }
|
||||
constexpr ALWAYS_INLINE int GetShareableInteger() const { return static_cast<int>(this->GetBits(8, 2)); }
|
||||
constexpr ALWAYS_INLINE int GetPageAttributeInteger() const { return static_cast<int>(this->GetBits(2, 3)); }
|
||||
constexpr ALWAYS_INLINE bool IsReadOnly() const { return this->GetBits(7, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsUserAccessible() const { return this->GetBits(6, 1) != 0; }
|
||||
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
|
||||
|
||||
@@ -98,8 +98,8 @@ namespace ams::kern::arch::arm64 {
|
||||
R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm));
|
||||
}
|
||||
|
||||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size) {
|
||||
R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size));
|
||||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping) {
|
||||
R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping));
|
||||
}
|
||||
|
||||
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace ams::kern::arch::arm64 {
|
||||
__asm__ __volatile__(
|
||||
" prfm pstl1keep, %[m_next_ticket]\n"
|
||||
"1:\n"
|
||||
" ldaxrh %w[tmp0], %[m_next_ticket]\n"
|
||||
" ldxrh %w[tmp0], %[m_next_ticket]\n"
|
||||
" add %w[tmp1], %w[tmp0], #0x1\n"
|
||||
" stxrh %w[got_lock], %w[tmp1], %[m_next_ticket]\n"
|
||||
" cbnz %w[got_lock], 1b\n"
|
||||
|
||||
@@ -25,12 +25,15 @@ namespace ams::kern::board::nintendo::nx {
|
||||
static constexpr size_t SecureAppletMemorySize = 4_MB;
|
||||
public:
|
||||
class Init : public KSystemControlBase::Init {
|
||||
private:
|
||||
friend class KSystemControlBase::Init;
|
||||
private:
|
||||
static void CpuOnImpl(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||
public:
|
||||
/* Initialization. */
|
||||
static size_t GetRealMemorySize();
|
||||
static size_t GetIntendedMemorySize();
|
||||
static bool ShouldIncreaseThreadResourceLimit();
|
||||
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||
static size_t GetApplicationPoolSize();
|
||||
static size_t GetAppletPoolSize();
|
||||
static size_t GetMinimumNonSecureSystemPoolSize();
|
||||
|
||||
@@ -26,6 +26,6 @@ namespace ams::kern::init {
|
||||
|
||||
static_assert(util::IsPowerOfTwo(alignof(KInitArguments)) && util::IsPowerOfTwo(sizeof(KInitArguments)));
|
||||
|
||||
KPhysicalAddress GetInitArgumentsAddress(s32 core_id);
|
||||
KInitArguments *GetInitArguments(s32 core_id);
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@
|
||||
#define MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP
|
||||
#define MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP
|
||||
|
||||
/* NOTE: In 16.0.0, Nintendo deleted the creation time field for KProcess, */
|
||||
/* but this may be useful for some debugging applications, and so can be. */
|
||||
/* re-enabled by toggling this define. */
|
||||
//#define MESOSPHERE_ENABLE_PROCESS_CREATION_TIME
|
||||
|
||||
/* NOTE: This enables fast class token storage using a class member. */
|
||||
/* This saves a virtual call when doing KAutoObject->DynCast<>(), */
|
||||
/* at the cost of storing class tokens inside the class object. */
|
||||
|
||||
@@ -40,12 +40,16 @@ namespace ams::kern {
|
||||
static uintptr_t GetAddressSpaceStart(size_t width, Type type);
|
||||
static size_t GetAddressSpaceSize(size_t width, Type type);
|
||||
|
||||
static void SetAddressSpaceSize(size_t width, Type type, size_t size);
|
||||
|
||||
constexpr KAddressSpaceInfo(size_t bw, size_t a, size_t s, Type t) : m_bit_width(bw), m_address(a), m_size(s), m_type(t) { /* ... */ }
|
||||
|
||||
constexpr size_t GetWidth() const { return m_bit_width; }
|
||||
constexpr size_t GetAddress() const { return m_address; }
|
||||
constexpr size_t GetSize() const { return m_size; }
|
||||
constexpr Type GetType() const { return m_type; }
|
||||
|
||||
constexpr void SetSize(size_t size) { m_size = size; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace ams::kern {
|
||||
return m_process_holder.Get();
|
||||
}
|
||||
private:
|
||||
void PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||
void PushDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params);
|
||||
void EnqueueDebugEventInfo(KEventInfo *info);
|
||||
|
||||
template<typename T> requires (std::same_as<T, ams::svc::lp64::DebugEventInfo> || std::same_as<T, ams::svc::ilp32::DebugEventInfo>)
|
||||
@@ -85,13 +85,13 @@ namespace ams::kern {
|
||||
/* NOTE: This is public/virtual override in Nintendo's kernel. */
|
||||
void OnFinalizeSynchronizationObject();
|
||||
private:
|
||||
static Result ProcessDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4);
|
||||
static Result ProcessDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params);
|
||||
public:
|
||||
static Result OnDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||
static Result OnDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params);
|
||||
static Result OnExitProcess(KProcess *process);
|
||||
static Result OnTerminateProcess(KProcess *process);
|
||||
static Result OnExitThread(KThread *thread);
|
||||
static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 thread_id);
|
||||
static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, u64 thread_id, const uintptr_t *params, size_t num_params);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace ams::kern {
|
||||
ams::svc::DebugException exception_type;
|
||||
s32 exception_data_count;
|
||||
uintptr_t exception_address;
|
||||
uintptr_t exception_data[4];
|
||||
uintptr_t exception_data[std::max<size_t>(4, cpu::NumCores)];
|
||||
};
|
||||
|
||||
struct InfoSystemCall {
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace ams::kern {
|
||||
class KIoPool final : public KAutoObjectWithSlabHeapAndContainer<KIoPool, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KIoPool, KAutoObject);
|
||||
private:
|
||||
using IoRegionList = util::IntrusiveListMemberTraits<&KIoRegion::m_pool_list_node>::ListType;
|
||||
using IoRegionTree = util::IntrusiveRedBlackTreeBaseTraits<KIoRegion>::TreeType<KIoRegion>;
|
||||
private:
|
||||
KLightLock m_lock;
|
||||
IoRegionList m_io_region_list;
|
||||
IoRegionTree m_io_region_tree;
|
||||
ams::svc::IoPoolType m_pool_type;
|
||||
bool m_is_initialized;
|
||||
public:
|
||||
|
||||
@@ -23,11 +23,30 @@ namespace ams::kern {
|
||||
class KProcess;
|
||||
class KIoPool;
|
||||
|
||||
class KIoRegion final : public KAutoObjectWithSlabHeapAndContainer<KIoRegion, KAutoObjectWithList> {
|
||||
class KIoRegion final : public KAutoObjectWithSlabHeapAndContainer<KIoRegion, KAutoObjectWithList>, public util::IntrusiveRedBlackTreeBaseNode<KIoRegion> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KIoRegion, KAutoObject);
|
||||
private:
|
||||
friend class KProcess;
|
||||
friend class KIoPool;
|
||||
public:
|
||||
using RedBlackKeyType = KPhysicalAddress;
|
||||
|
||||
static constexpr ALWAYS_INLINE RedBlackKeyType GetRedBlackKey(const RedBlackKeyType &v) { return v; }
|
||||
static constexpr ALWAYS_INLINE RedBlackKeyType GetRedBlackKey(const KIoRegion &v) { return v.GetAddress(); }
|
||||
|
||||
template<typename T> requires (std::same_as<T, KIoRegion> || std::same_as<T, RedBlackKeyType>)
|
||||
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KIoRegion &rhs) {
|
||||
const RedBlackKeyType lval = GetRedBlackKey(lhs);
|
||||
const RedBlackKeyType rval = GetRedBlackKey(rhs);
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
private:
|
||||
KLightLock m_lock;
|
||||
KIoPool *m_pool;
|
||||
@@ -38,7 +57,6 @@ namespace ams::kern {
|
||||
bool m_is_initialized;
|
||||
bool m_is_mapped;
|
||||
util::IntrusiveListNode m_process_list_node;
|
||||
util::IntrusiveListNode m_pool_list_node;
|
||||
public:
|
||||
explicit KIoRegion() : m_pool(nullptr), m_is_initialized(false) { /* ... */ }
|
||||
|
||||
@@ -51,12 +69,25 @@ namespace ams::kern {
|
||||
Result Map(KProcessAddress address, size_t size, ams::svc::MemoryPermission map_perm);
|
||||
Result Unmap(KProcessAddress address, size_t size);
|
||||
|
||||
bool Overlaps(KPhysicalAddress address, size_t size) const {
|
||||
constexpr bool Overlaps(KPhysicalAddress address, size_t size) const {
|
||||
return m_physical_address <= (address + size - 1) && address <= (m_physical_address + m_size - 1);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KPhysicalAddress GetAddress() const { return m_physical_address; }
|
||||
ALWAYS_INLINE size_t GetSize() const { return m_size; }
|
||||
constexpr ALWAYS_INLINE KPhysicalAddress GetAddress() const { return m_physical_address; }
|
||||
constexpr ALWAYS_INLINE size_t GetSize() const { return m_size; }
|
||||
|
||||
constexpr uintptr_t GetHint() const {
|
||||
/* TODO: Is this architecture specific? */
|
||||
if (m_size >= 2_MB) {
|
||||
return GetInteger(m_physical_address) & (2_MB - 1);
|
||||
} else if (m_size >= 64_KB) {
|
||||
return GetInteger(m_physical_address) & (64_KB - 1);
|
||||
} else if (m_size >= 4_KB) {
|
||||
return GetInteger(m_physical_address) & (4_KB - 1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -183,10 +183,10 @@ namespace ams::kern {
|
||||
return std::make_tuple(total_size, kernel_size);
|
||||
}
|
||||
|
||||
static void InitializeLinearMemoryAddresses(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start) {
|
||||
static void InitializeLinearMemoryAddresses(u64 phys_to_virt_diff) {
|
||||
/* 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);
|
||||
s_linear_phys_to_virt_diff = phys_to_virt_diff;
|
||||
s_linear_virt_to_phys_diff = -phys_to_virt_diff;
|
||||
}
|
||||
|
||||
static void InitializeLinearMemoryRegionTrees();
|
||||
|
||||
@@ -72,13 +72,14 @@ namespace ams::kern {
|
||||
};
|
||||
|
||||
enum OperationType {
|
||||
OperationType_Map = 0,
|
||||
OperationType_MapFirst = 1,
|
||||
OperationType_MapGroup = 2,
|
||||
OperationType_Unmap = 3,
|
||||
OperationType_ChangePermissions = 4,
|
||||
OperationType_ChangePermissionsAndRefresh = 5,
|
||||
OperationType_Separate = 6,
|
||||
OperationType_Map = 0,
|
||||
OperationType_MapFirst = 1,
|
||||
OperationType_MapGroup = 2,
|
||||
OperationType_Unmap = 3,
|
||||
OperationType_ChangePermissions = 4,
|
||||
OperationType_ChangePermissionsAndRefresh = 5,
|
||||
OperationType_ChangePermissionsAndRefreshAndFlush = 6,
|
||||
OperationType_Separate = 7,
|
||||
};
|
||||
|
||||
static constexpr size_t MaxPhysicalMapAlignment = 1_GB;
|
||||
@@ -363,7 +364,7 @@ namespace ams::kern {
|
||||
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
|
||||
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
|
||||
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping, ams::svc::MemoryPermission perm);
|
||||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size);
|
||||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping);
|
||||
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
|
||||
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
|
||||
Result MapInsecureMemory(KProcessAddress address, size_t size);
|
||||
|
||||
@@ -84,7 +84,9 @@ namespace ams::kern {
|
||||
KCapabilities m_capabilities;
|
||||
ams::svc::ProgramId m_program_id;
|
||||
u64 m_process_id;
|
||||
#if defined(MESOSPHERE_ENABLE_PROCESS_CREATION_TIME)
|
||||
s64 m_creation_time;
|
||||
#endif
|
||||
KProcessAddress m_code_address;
|
||||
size_t m_code_size;
|
||||
size_t m_main_thread_stack_size;
|
||||
@@ -108,6 +110,7 @@ namespace ams::kern {
|
||||
KWaitObject m_wait_object;
|
||||
KThread *m_running_threads[cpu::NumCores];
|
||||
u64 m_running_thread_idle_counts[cpu::NumCores];
|
||||
u64 m_running_thread_switch_counts[cpu::NumCores];
|
||||
KThread *m_pinned_threads[cpu::NumCores];
|
||||
util::Atomic<s64> m_cpu_time;
|
||||
util::Atomic<s64> m_num_process_switches;
|
||||
@@ -285,9 +288,10 @@ namespace ams::kern {
|
||||
return m_system_resource->IsSecureResource() ? static_cast<KSecureSystemResource *>(m_system_resource)->GetUsedSize() : 0;
|
||||
}
|
||||
|
||||
void SetRunningThread(s32 core, KThread *thread, u64 idle_count) {
|
||||
m_running_threads[core] = thread;
|
||||
m_running_thread_idle_counts[core] = idle_count;
|
||||
void SetRunningThread(s32 core, KThread *thread, u64 idle_count, u64 switch_count) {
|
||||
m_running_threads[core] = thread;
|
||||
m_running_thread_idle_counts[core] = idle_count;
|
||||
m_running_thread_switch_counts[core] = switch_count;
|
||||
}
|
||||
|
||||
void ClearRunningThread(KThread *thread) {
|
||||
@@ -306,6 +310,7 @@ namespace ams::kern {
|
||||
|
||||
constexpr KThread *GetRunningThread(s32 core) const { return m_running_threads[core]; }
|
||||
constexpr u64 GetRunningThreadIdleCount(s32 core) const { return m_running_thread_idle_counts[core]; }
|
||||
constexpr u64 GetRunningThreadSwitchCount(s32 core) const { return m_running_thread_switch_counts[core]; }
|
||||
|
||||
void RegisterThread(KThread *thread);
|
||||
void UnregisterThread(KThread *thread);
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace ams::kern {
|
||||
bool interrupt_task_runnable{false};
|
||||
bool should_count_idle{false};
|
||||
u64 idle_count{0};
|
||||
u64 switch_count{0};
|
||||
KThread *highest_priority_thread{nullptr};
|
||||
void *idle_thread_stack{nullptr};
|
||||
KThread *prev_thread{nullptr};
|
||||
@@ -67,6 +68,7 @@ namespace ams::kern {
|
||||
m_state.interrupt_task_runnable = false;
|
||||
m_state.should_count_idle = false;
|
||||
m_state.idle_count = 0;
|
||||
m_state.switch_count = 0;
|
||||
m_state.idle_thread_stack = nullptr;
|
||||
m_state.highest_priority_thread = nullptr;
|
||||
m_state.prev_thread = nullptr;
|
||||
@@ -93,6 +95,10 @@ namespace ams::kern {
|
||||
return m_state.idle_count;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GetSwitchCount() const {
|
||||
return m_state.switch_count;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KThread *GetIdleThread() const {
|
||||
return m_idle_thread;
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace ams::kern {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
if (this->IsLockedByCurrentThread()) {
|
||||
/* If we already own the lock, we can just increment the count. */
|
||||
/* If we already own the lock, the lock count should be > 0. */
|
||||
/* For debug, ensure this is true. */
|
||||
MESOSPHERE_ASSERT(m_lock_count > 0);
|
||||
m_lock_count++;
|
||||
} else {
|
||||
/* Otherwise, we want to disable scheduling and acquire the spinlock. */
|
||||
SchedulerType::DisableScheduling();
|
||||
@@ -61,10 +61,12 @@ namespace ams::kern {
|
||||
MESOSPHERE_ASSERT(m_lock_count == 0);
|
||||
MESOSPHERE_ASSERT(m_owner_thread == nullptr);
|
||||
|
||||
/* Increment count, take ownership. */
|
||||
m_lock_count = 1;
|
||||
/* Take ownership of the lock. */
|
||||
m_owner_thread = GetCurrentThreadPointer();
|
||||
}
|
||||
|
||||
/* Increment the lock count. */
|
||||
m_lock_count++;
|
||||
}
|
||||
|
||||
MESOSPHERE_ALWAYS_INLINE_IF_RELEASE void Unlock() {
|
||||
|
||||
@@ -21,6 +21,12 @@ namespace ams::kern {
|
||||
|
||||
struct InitialProcessBinaryLayout;
|
||||
|
||||
namespace init {
|
||||
|
||||
struct KInitArguments;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::kern {
|
||||
@@ -40,6 +46,8 @@ namespace ams::kern {
|
||||
static constinit inline KSpinLock s_random_lock;
|
||||
public:
|
||||
class Init {
|
||||
private:
|
||||
static void CpuOnImpl(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||
public:
|
||||
/* Initialization. */
|
||||
static size_t GetRealMemorySize();
|
||||
@@ -47,7 +55,7 @@ namespace ams::kern {
|
||||
static KPhysicalAddress GetKernelPhysicalBaseAddress(KPhysicalAddress base_address);
|
||||
static void GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out);
|
||||
static bool ShouldIncreaseThreadResourceLimit();
|
||||
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||
static void TurnOnCpu(u64 core_id, const ams::kern::init::KInitArguments *args);
|
||||
static size_t GetApplicationPoolSize();
|
||||
static size_t GetAppletPoolSize();
|
||||
static size_t GetMinimumNonSecureSystemPoolSize();
|
||||
@@ -57,9 +65,11 @@ namespace ams::kern {
|
||||
static void GenerateRandom(u64 *dst, size_t count);
|
||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||
};
|
||||
protected:
|
||||
static NOINLINE void InitializePhase1Base(u64 seed);
|
||||
public:
|
||||
/* Initialization. */
|
||||
static NOINLINE void InitializePhase1(bool skip_target_system = false);
|
||||
static NOINLINE void InitializePhase1();
|
||||
static NOINLINE void InitializePhase2();
|
||||
static NOINLINE u32 GetCreateProcessMemoryPool();
|
||||
|
||||
|
||||
@@ -108,10 +108,12 @@ namespace ams::kern {
|
||||
u8 exception_flags;
|
||||
bool is_pinned;
|
||||
u8 reserved_2f;
|
||||
u8 reserved_30[0x10];
|
||||
KThreadContext context;
|
||||
};
|
||||
|
||||
static_assert(util::IsAligned(AMS_OFFSETOF(StackParameters, context), 0x10));
|
||||
static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE);
|
||||
|
||||
static_assert(AMS_OFFSETOF(StackParameters, svc_access_flags) == THREAD_STACK_PARAMETERS_SVC_PERMISSION);
|
||||
static_assert(AMS_OFFSETOF(StackParameters, caller_save_fpu_registers) == THREAD_STACK_PARAMETERS_CALLER_SAVE_FPU_REGISTERS);
|
||||
@@ -123,8 +125,10 @@ namespace ams::kern {
|
||||
static_assert(AMS_OFFSETOF(StackParameters, exception_flags) == THREAD_STACK_PARAMETERS_EXCEPTION_FLAGS);
|
||||
static_assert(AMS_OFFSETOF(StackParameters, is_pinned) == THREAD_STACK_PARAMETERS_IS_PINNED);
|
||||
static_assert(AMS_OFFSETOF(StackParameters, reserved_2f) == THREAD_STACK_PARAMETERS_RESERVED_2F);
|
||||
static_assert(AMS_OFFSETOF(StackParameters, reserved_30) == THREAD_STACK_PARAMETERS_RESERVED_30);
|
||||
static_assert(AMS_OFFSETOF(StackParameters, context) == THREAD_STACK_PARAMETERS_THREAD_CONTEXT);
|
||||
|
||||
|
||||
static_assert(ExceptionFlag_IsCallingSvc == THREAD_EXCEPTION_FLAG_IS_CALLING_SVC);
|
||||
static_assert(ExceptionFlag_IsInExceptionHandler == THREAD_EXCEPTION_FLAG_IS_IN_EXCEPTION_HANDLER);
|
||||
static_assert(ExceptionFlag_IsFpuContextRestoreNeeded == THREAD_EXCEPTION_FLAG_IS_FPU_CONTEXT_RESTORE_NEEDED);
|
||||
@@ -197,6 +201,28 @@ namespace ams::kern {
|
||||
};
|
||||
static_assert(ams::util::HasRedBlackKeyType<ConditionVariableComparator>);
|
||||
static_assert(std::same_as<ams::util::RedBlackKeyType<ConditionVariableComparator, void>, ConditionVariableComparator::RedBlackKeyType>);
|
||||
|
||||
struct LockWithPriorityInheritanceComparator {
|
||||
struct RedBlackKeyType {
|
||||
s32 m_priority;
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetPriority() const {
|
||||
return m_priority;
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
if (lhs.GetPriority() < rhs.GetPriority()) {
|
||||
/* And then by priority. */
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(ams::util::HasRedBlackKeyType<LockWithPriorityInheritanceComparator>);
|
||||
static_assert(std::same_as<ams::util::RedBlackKeyType<LockWithPriorityInheritanceComparator, void>, LockWithPriorityInheritanceComparator::RedBlackKeyType>);
|
||||
private:
|
||||
util::IntrusiveListNode m_process_list_node;
|
||||
util::IntrusiveRedBlackTreeNode m_condvar_arbiter_tree_node;
|
||||
@@ -205,6 +231,67 @@ namespace ams::kern {
|
||||
using ConditionVariableThreadTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&KThread::m_condvar_arbiter_tree_node>;
|
||||
using ConditionVariableThreadTree = ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
|
||||
|
||||
using LockWithPriorityInheritanceThreadTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&KThread::m_condvar_arbiter_tree_node>;
|
||||
using LockWithPriorityInheritanceThreadTree = ConditionVariableThreadTreeTraits::TreeType<LockWithPriorityInheritanceComparator>;
|
||||
public:
|
||||
class LockWithPriorityInheritanceInfo : public KSlabAllocated<LockWithPriorityInheritanceInfo>, public util::IntrusiveListBaseNode<LockWithPriorityInheritanceInfo> {
|
||||
private:
|
||||
LockWithPriorityInheritanceThreadTree m_tree;
|
||||
KProcessAddress m_address_key;
|
||||
KThread *m_owner;
|
||||
u32 m_waiter_count;
|
||||
public:
|
||||
constexpr LockWithPriorityInheritanceInfo() : m_tree(), m_address_key(Null<KProcessAddress>), m_owner(nullptr), m_waiter_count() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
static LockWithPriorityInheritanceInfo *Create(KProcessAddress address_key) {
|
||||
/* Create a new lock info. */
|
||||
auto *new_lock = LockWithPriorityInheritanceInfo::Allocate();
|
||||
MESOSPHERE_ABORT_UNLESS(new_lock != nullptr);
|
||||
|
||||
/* Set the new lock's address key. */
|
||||
new_lock->m_address_key = address_key;
|
||||
|
||||
return new_lock;
|
||||
}
|
||||
|
||||
void SetOwner(KThread *new_owner) {
|
||||
/* Set new owner. */
|
||||
m_owner = new_owner;
|
||||
}
|
||||
|
||||
void AddWaiter(KThread *waiter) {
|
||||
/* Insert the waiter. */
|
||||
m_tree.insert(*waiter);
|
||||
m_waiter_count++;
|
||||
|
||||
waiter->SetWaitingLockInfo(this);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool RemoveWaiter(KThread *waiter) {
|
||||
m_tree.erase(m_tree.iterator_to(*waiter));
|
||||
|
||||
waiter->SetWaitingLockInfo(nullptr);
|
||||
|
||||
return (--m_waiter_count) == 0;
|
||||
}
|
||||
|
||||
KThread *GetHighestPriorityWaiter() { return std::addressof(m_tree.front()); }
|
||||
const KThread *GetHighestPriorityWaiter() const { return std::addressof(m_tree.front()); }
|
||||
|
||||
LockWithPriorityInheritanceThreadTree &GetThreadTree() { return m_tree; }
|
||||
const LockWithPriorityInheritanceThreadTree &GetThreadTree() const { return m_tree; }
|
||||
|
||||
constexpr KProcessAddress GetAddressKey() const { return m_address_key; }
|
||||
|
||||
constexpr KThread *GetOwner() const { return m_owner; }
|
||||
|
||||
constexpr u32 GetWaiterCount() const { return m_waiter_count; }
|
||||
};
|
||||
private:
|
||||
using LockWithPriorityInheritanceInfoList = util::IntrusiveListBaseTraits<LockWithPriorityInheritanceInfo>::ListType;
|
||||
|
||||
ConditionVariableThreadTree *m_condvar_tree;
|
||||
uintptr_t m_condvar_key;
|
||||
alignas(16) KThreadContext::CallerSaveFpuRegisters m_caller_save_fpu_registers;
|
||||
@@ -224,9 +311,9 @@ namespace ams::kern {
|
||||
s64 m_last_scheduled_tick;
|
||||
QueueEntry m_per_core_priority_queue_entry[cpu::NumCores];
|
||||
KThreadQueue *m_wait_queue;
|
||||
WaiterList m_waiter_list;
|
||||
LockWithPriorityInheritanceInfoList m_held_lock_info_list;
|
||||
LockWithPriorityInheritanceInfo *m_waiting_lock_info;
|
||||
WaiterList m_pinned_waiter_list;
|
||||
KThread *m_lock_owner;
|
||||
uintptr_t m_debug_params[3];
|
||||
KAutoObject *m_closed_object;
|
||||
u32 m_address_key_value;
|
||||
@@ -260,8 +347,8 @@ namespace ams::kern {
|
||||
m_process_list_node{}, m_condvar_arbiter_tree_node{util::ConstantInitialize}, m_priority{-1}, m_condvar_tree{}, m_condvar_key{},
|
||||
m_caller_save_fpu_registers{}, m_virtual_affinity_mask{}, m_physical_affinity_mask{}, m_thread_id{}, m_cpu_time{0}, m_address_key{Null<KProcessAddress>}, m_parent{},
|
||||
m_kernel_stack_top{}, m_light_ipc_data{}, m_tls_address{Null<KProcessAddress>}, m_tls_heap_address{}, m_activity_pause_lock{}, m_sync_object_buffer{util::ConstantInitialize},
|
||||
m_schedule_count{}, m_last_scheduled_tick{}, m_per_core_priority_queue_entry{}, m_wait_queue{}, m_waiter_list{}, m_pinned_waiter_list{},
|
||||
m_lock_owner{}, m_debug_params{}, m_closed_object{}, m_address_key_value{}, m_suspend_request_flags{}, m_suspend_allowed_flags{}, m_synced_index{},
|
||||
m_schedule_count{}, m_last_scheduled_tick{}, m_per_core_priority_queue_entry{}, m_wait_queue{}, m_held_lock_info_list{}, m_waiting_lock_info{},
|
||||
m_pinned_waiter_list{}, m_debug_params{}, m_closed_object{}, m_address_key_value{}, m_suspend_request_flags{}, m_suspend_allowed_flags{}, m_synced_index{},
|
||||
m_wait_result{svc::ResultNoSynchronizationObject()}, m_debug_exception_result{ResultSuccess()}, m_base_priority{}, m_base_priority_on_unpin{},
|
||||
m_physical_ideal_core_id{}, m_virtual_ideal_core_id{}, m_num_kernel_waiters{}, m_current_core_id{}, m_core_id{}, m_original_physical_affinity_mask{},
|
||||
m_original_physical_ideal_core_id{}, m_num_core_migration_disables{}, m_thread_state{}, m_termination_requested{false}, m_wait_cancelled{},
|
||||
@@ -407,6 +494,10 @@ namespace ams::kern {
|
||||
void ClearUsermodeExceptionSvcPermissions();
|
||||
private:
|
||||
void UpdateState();
|
||||
|
||||
ALWAYS_INLINE void AddHeldLock(LockWithPriorityInheritanceInfo *lock_info);
|
||||
ALWAYS_INLINE LockWithPriorityInheritanceInfo *FindHeldLock(KProcessAddress address_key);
|
||||
|
||||
ALWAYS_INLINE void AddWaiterImpl(KThread *thread);
|
||||
ALWAYS_INLINE void RemoveWaiterImpl(KThread *thread);
|
||||
ALWAYS_INLINE static void RestorePriority(KThread *thread);
|
||||
@@ -441,6 +532,8 @@ namespace ams::kern {
|
||||
constexpr uintptr_t GetAddressArbiterKey() const { return m_condvar_key; }
|
||||
|
||||
constexpr void SetConditionVariable(ConditionVariableThreadTree *tree, KProcessAddress address, uintptr_t cv_key, u32 value) {
|
||||
MESOSPHERE_ASSERT(m_waiting_lock_info == nullptr);
|
||||
|
||||
m_condvar_tree = tree;
|
||||
m_condvar_key = cv_key;
|
||||
m_address_key = address;
|
||||
@@ -456,6 +549,8 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
constexpr void SetAddressArbiter(ConditionVariableThreadTree *tree, uintptr_t address) {
|
||||
MESOSPHERE_ASSERT(m_waiting_lock_info == nullptr);
|
||||
|
||||
m_condvar_tree = tree;
|
||||
m_condvar_key = address;
|
||||
}
|
||||
@@ -491,15 +586,17 @@ namespace ams::kern {
|
||||
|
||||
void AddWaiter(KThread *thread);
|
||||
void RemoveWaiter(KThread *thread);
|
||||
KThread *RemoveWaiterByKey(s32 *out_num_waiters, KProcessAddress key);
|
||||
KThread *RemoveWaiterByKey(bool *out_has_waiters, KProcessAddress key);
|
||||
|
||||
constexpr KProcessAddress GetAddressKey() const { return m_address_key; }
|
||||
constexpr u32 GetAddressKeyValue() const { return m_address_key_value; }
|
||||
constexpr void SetAddressKey(KProcessAddress key) { m_address_key = key; }
|
||||
constexpr void SetAddressKey(KProcessAddress key, u32 val) { m_address_key = key; m_address_key_value = val; }
|
||||
constexpr void SetAddressKey(KProcessAddress key) { MESOSPHERE_ASSERT(m_waiting_lock_info == nullptr); m_address_key = key; }
|
||||
constexpr void SetAddressKey(KProcessAddress key, u32 val) { MESOSPHERE_ASSERT(m_waiting_lock_info == nullptr); m_address_key = key; m_address_key_value = val; }
|
||||
|
||||
constexpr void SetLockOwner(KThread *owner) { m_lock_owner = owner; }
|
||||
constexpr KThread *GetLockOwner() const { return m_lock_owner; }
|
||||
constexpr void SetWaitingLockInfo(LockWithPriorityInheritanceInfo *lock) { m_waiting_lock_info = lock; }
|
||||
constexpr LockWithPriorityInheritanceInfo *GetWaitingLockInfo() { return m_waiting_lock_info; }
|
||||
|
||||
constexpr KThread *GetLockOwner() const { return m_waiting_lock_info != nullptr ? m_waiting_lock_info->GetOwner() : nullptr; }
|
||||
|
||||
constexpr void ClearWaitQueue() { m_wait_queue = nullptr; }
|
||||
|
||||
@@ -529,8 +626,6 @@ namespace ams::kern {
|
||||
constexpr u32 *GetLightSessionData() const { return m_light_ipc_data; }
|
||||
constexpr void SetLightSessionData(u32 *data) { m_light_ipc_data = data; }
|
||||
|
||||
bool HasWaiters() const { return !m_waiter_list.empty(); }
|
||||
|
||||
constexpr s64 GetLastScheduledTick() const { return m_last_scheduled_tick; }
|
||||
constexpr void SetLastScheduledTick(s64 tick) { m_last_scheduled_tick = tick; }
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 El0PsrMask = 0xFF0FFE20;
|
||||
|
||||
enum EsrEc : u32 {
|
||||
EsrEc_Unknown = 0b000000,
|
||||
EsrEc_WaitForInterruptOrEvent = 0b000001,
|
||||
@@ -134,7 +132,7 @@ namespace ams::kern::arch::arm64 {
|
||||
info->sp = context->sp;
|
||||
info->lr = context->x[30];
|
||||
info->pc = context->pc;
|
||||
info->pstate = (context->psr & El0PsrMask);
|
||||
info->pstate = (context->psr & cpu::El0Aarch64PsrMask);
|
||||
info->afsr0 = afsr0;
|
||||
info->afsr1 = afsr1;
|
||||
info->esr = esr;
|
||||
@@ -151,7 +149,7 @@ namespace ams::kern::arch::arm64 {
|
||||
info->pc = context->pc;
|
||||
info->flags = 1;
|
||||
|
||||
info->status_64.pstate = (context->psr & El0PsrMask);
|
||||
info->status_64.pstate = (context->psr & cpu::El0Aarch32PsrMask);
|
||||
info->status_64.afsr0 = afsr0;
|
||||
info->status_64.afsr1 = afsr1;
|
||||
info->status_64.esr = esr;
|
||||
@@ -231,73 +229,71 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
{
|
||||
/* Collect additional information based on the ec. */
|
||||
ams::svc::DebugException exception;
|
||||
uintptr_t param2 = 0;
|
||||
uintptr_t param3 = 0;
|
||||
uintptr_t params[3] = {};
|
||||
switch (ec) {
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedInstruction;
|
||||
param2 = far;
|
||||
param3 = data;
|
||||
params[0] = ams::svc::DebugException_UndefinedInstruction;
|
||||
params[1] = far;
|
||||
params[2] = data;
|
||||
}
|
||||
break;
|
||||
case EsrEc_PcAlignmentFault:
|
||||
case EsrEc_SpAlignmentFault:
|
||||
{
|
||||
exception = ams::svc::DebugException_AlignmentFault;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_AlignmentFault;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedSystemCall;
|
||||
param2 = far;
|
||||
param3 = (esr & 0xFF);
|
||||
params[0] = ams::svc::DebugException_UndefinedSystemCall;
|
||||
params[1] = far;
|
||||
params[2] = (esr & 0xFF);
|
||||
}
|
||||
break;
|
||||
case EsrEc_BreakPointEl0:
|
||||
case EsrEc_SoftwareStepEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_BreakPoint;
|
||||
param2 = far;
|
||||
param3 = ams::svc::BreakPointType_HardwareInstruction;
|
||||
params[0] = ams::svc::DebugException_BreakPoint;
|
||||
params[1] = far;
|
||||
params[2] = ams::svc::BreakPointType_HardwareInstruction;
|
||||
}
|
||||
break;
|
||||
case EsrEc_WatchPointEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_BreakPoint;
|
||||
param2 = far;
|
||||
param3 = ams::svc::BreakPointType_HardwareData;
|
||||
params[0] = ams::svc::DebugException_BreakPoint;
|
||||
params[1] = far;
|
||||
params[2] = ams::svc::BreakPointType_HardwareData;
|
||||
}
|
||||
break;
|
||||
case EsrEc_SErrorInterrupt:
|
||||
{
|
||||
exception = ams::svc::DebugException_MemorySystemError;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_MemorySystemError;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_InstructionAbortEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_InstructionAbort;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_InstructionAbort;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_DataAbortEl0:
|
||||
default:
|
||||
{
|
||||
exception = ams::svc::DebugException_DataAbort;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_DataAbort;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process the debug event. */
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
|
||||
|
||||
/* If we should stop processing the exception, do so. */
|
||||
if (svc::ResultStopProcessingException::Includes(result)) {
|
||||
@@ -342,7 +338,7 @@ namespace ams::kern::arch::arm64 {
|
||||
/* If the SVC is handled, handle it. */
|
||||
if (!svc::ResultNotHandled::Includes(result)) {
|
||||
/* If we successfully enter jit debug, stop processing the exception. */
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, static_cast<ams::svc::DebugException>(params[0]), params[1], params[2])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -399,7 +395,7 @@ namespace ams::kern::arch::arm64 {
|
||||
e_ctx->x[30] = info.info64.lr;
|
||||
e_ctx->sp = info.info64.sp;
|
||||
e_ctx->pc = info.info64.pc;
|
||||
e_ctx->psr = (info.info64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask);
|
||||
e_ctx->psr = (info.info64.pstate & cpu::El0Aarch64PsrMask) | (e_ctx->psr & ~cpu::El0Aarch64PsrMask);
|
||||
} else {
|
||||
for (size_t i = 0; i < util::size(info.info32.r); ++i) {
|
||||
e_ctx->x[i] = info.info32.r[i];
|
||||
@@ -407,7 +403,7 @@ namespace ams::kern::arch::arm64 {
|
||||
e_ctx->x[14] = info.info32.lr;
|
||||
e_ctx->x[13] = info.info32.sp;
|
||||
e_ctx->pc = info.info32.pc;
|
||||
e_ctx->psr = (info.info32.status_64.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask);
|
||||
e_ctx->psr = (info.info32.status_64.pstate & cpu::El0Aarch32PsrMask) | (e_ctx->psr & ~cpu::El0Aarch32PsrMask);
|
||||
}
|
||||
|
||||
/* Note that PC was adjusted. */
|
||||
@@ -422,58 +418,56 @@ namespace ams::kern::arch::arm64 {
|
||||
GetCurrentThread().RestoreDebugParams(std::addressof(far), std::addressof(esr), std::addressof(data));
|
||||
|
||||
/* Collect additional information based on the ec. */
|
||||
ams::svc::DebugException exception;
|
||||
uintptr_t param2 = 0;
|
||||
uintptr_t param3 = 0;
|
||||
uintptr_t params[3] = {};
|
||||
switch ((esr >> 26) & 0x3F) {
|
||||
case EsrEc_Unknown:
|
||||
case EsrEc_IllegalExecution:
|
||||
case EsrEc_BkptInstruction:
|
||||
case EsrEc_BrkInstruction:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedInstruction;
|
||||
param2 = far;
|
||||
param3 = data;
|
||||
params[0] = ams::svc::DebugException_UndefinedInstruction;
|
||||
params[1] = far;
|
||||
params[2] = data;
|
||||
}
|
||||
break;
|
||||
case EsrEc_PcAlignmentFault:
|
||||
case EsrEc_SpAlignmentFault:
|
||||
{
|
||||
exception = ams::svc::DebugException_AlignmentFault;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_AlignmentFault;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_Svc32:
|
||||
case EsrEc_Svc64:
|
||||
{
|
||||
exception = ams::svc::DebugException_UndefinedSystemCall;
|
||||
param2 = far;
|
||||
param3 = (esr & 0xFF);
|
||||
params[0] = ams::svc::DebugException_UndefinedSystemCall;
|
||||
params[1] = far;
|
||||
params[2] = (esr & 0xFF);
|
||||
}
|
||||
break;
|
||||
case EsrEc_SErrorInterrupt:
|
||||
{
|
||||
exception = ams::svc::DebugException_MemorySystemError;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_MemorySystemError;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_InstructionAbortEl0:
|
||||
{
|
||||
exception = ams::svc::DebugException_InstructionAbort;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_InstructionAbort;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
case EsrEc_DataAbortEl0:
|
||||
default:
|
||||
{
|
||||
exception = ams::svc::DebugException_DataAbort;
|
||||
param2 = far;
|
||||
params[0] = ams::svc::DebugException_DataAbort;
|
||||
params[1] = far;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process the debug event. */
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, exception, param2, param3);
|
||||
Result result = KDebug::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
|
||||
|
||||
/* If the SVC is handled, handle it. */
|
||||
if (!svc::ResultNotHandled::Includes(result)) {
|
||||
@@ -483,7 +477,7 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
|
||||
/* If we successfully enter jit debug, restore. */
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, exception, param2, param3)) {
|
||||
if (cur_process.EnterJitDebug(ams::svc::DebugEvent_Exception, static_cast<ams::svc::DebugException>(params[0]), params[1], params[2])) {
|
||||
svc::RestoreContext(reinterpret_cast<uintptr_t>(e_ctx));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
static_assert(ForbiddenWatchPointFlagsMask == 0xFFFFFFFF00F0E006ul);
|
||||
|
||||
constexpr inline u32 El0PsrMask = 0xFF0FFE20;
|
||||
|
||||
}
|
||||
|
||||
uintptr_t KDebug::GetProgramCounter(const KThread &thread) {
|
||||
@@ -104,7 +102,7 @@ namespace ams::kern::arch::arm64 {
|
||||
out->lr = e_ctx->x[30];
|
||||
out->sp = e_ctx->sp;
|
||||
out->pc = e_ctx->pc;
|
||||
out->pstate = (e_ctx->psr & El0PsrMask);
|
||||
out->pstate = (e_ctx->psr & cpu::El0Aarch64PsrMask);
|
||||
|
||||
/* Adjust PC if we should. */
|
||||
if (e_ctx->write == 0 && thread->IsCallingSvc()) {
|
||||
@@ -119,7 +117,7 @@ namespace ams::kern::arch::arm64 {
|
||||
out->lr = 0;
|
||||
out->sp = 0;
|
||||
out->pc = e_ctx->pc;
|
||||
out->pstate = (e_ctx->psr & El0PsrMask);
|
||||
out->pstate = (e_ctx->psr & cpu::El0Aarch32PsrMask);
|
||||
|
||||
/* Adjust PC if we should. */
|
||||
if (e_ctx->write == 0 && thread->IsCallingSvc()) {
|
||||
@@ -166,7 +164,7 @@ namespace ams::kern::arch::arm64 {
|
||||
e_ctx->x[30] = ctx.lr;
|
||||
e_ctx->sp = ctx.sp;
|
||||
e_ctx->pc = ctx.pc;
|
||||
e_ctx->psr = ((ctx.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask));
|
||||
e_ctx->psr = ((ctx.pstate & cpu::El0Aarch64PsrMask) | (e_ctx->psr & ~cpu::El0Aarch64PsrMask));
|
||||
e_ctx->tpidr = ctx.tpidr;
|
||||
} else {
|
||||
e_ctx->x[13] = static_cast<u32>(ctx.r[13]);
|
||||
@@ -174,7 +172,7 @@ namespace ams::kern::arch::arm64 {
|
||||
e_ctx->x[30] = 0;
|
||||
e_ctx->sp = 0;
|
||||
e_ctx->pc = static_cast<u32>(ctx.pc);
|
||||
e_ctx->psr = ((ctx.pstate & El0PsrMask) | (e_ctx->psr & ~El0PsrMask));
|
||||
e_ctx->psr = ((ctx.pstate & cpu::El0Aarch32PsrMask) | (e_ctx->psr & ~cpu::El0Aarch32PsrMask));
|
||||
e_ctx->tpidr = ctx.tpidr;
|
||||
}
|
||||
}
|
||||
@@ -251,7 +249,8 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
|
||||
Result KDebug::BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
|
||||
R_RETURN(KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size));
|
||||
const uintptr_t params[5] = { ams::svc::DebugException_UserBreak, GetProgramCounter(GetCurrentThread()), break_reason, address, size };
|
||||
R_RETURN(KDebugBase::OnDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params)));
|
||||
}
|
||||
|
||||
#define MESOSPHERE_SET_HW_BREAK_POINT(ID, FLAGS, VALUE) \
|
||||
|
||||
@@ -378,9 +378,11 @@ namespace ams::kern::arch::arm64 {
|
||||
case OperationType_MapFirst:
|
||||
R_RETURN(this->MapContiguous(virt_addr, phys_addr, num_pages, entry_template, properties.disable_merge_attributes == DisableMergeAttribute_DisableHead, operation != OperationType_MapFirst, page_list, reuse_ll));
|
||||
case OperationType_ChangePermissions:
|
||||
R_RETURN(this->ChangePermissions(virt_addr, num_pages, entry_template, properties.disable_merge_attributes, false, page_list, reuse_ll));
|
||||
R_RETURN(this->ChangePermissions(virt_addr, num_pages, entry_template, properties.disable_merge_attributes, false, false, page_list, reuse_ll));
|
||||
case OperationType_ChangePermissionsAndRefresh:
|
||||
R_RETURN(this->ChangePermissions(virt_addr, num_pages, entry_template, properties.disable_merge_attributes, true, page_list, reuse_ll));
|
||||
R_RETURN(this->ChangePermissions(virt_addr, num_pages, entry_template, properties.disable_merge_attributes, true, false, page_list, reuse_ll));
|
||||
case OperationType_ChangePermissionsAndRefreshAndFlush:
|
||||
R_RETURN(this->ChangePermissions(virt_addr, num_pages, entry_template, properties.disable_merge_attributes, true, true, page_list, reuse_ll));
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
@@ -1233,7 +1235,7 @@ namespace ams::kern::arch::arm64 {
|
||||
R_RETURN(this->SeparatePagesImpl(virt_addr, block_size, page_list, reuse_ll));
|
||||
}
|
||||
|
||||
Result KPageTable::ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll) {
|
||||
Result KPageTable::ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, DisableMergeAttribute disable_merge_attr, bool refresh_mapping, bool flush_mapping, PageLinkedList *page_list, bool reuse_ll) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
/* Separate pages before we change permissions. */
|
||||
@@ -1451,8 +1453,8 @@ namespace ams::kern::arch::arm64 {
|
||||
KScopedSchedulerLock sl;
|
||||
}
|
||||
|
||||
/* Finally, apply the changes as directed, flushing the mappings before they're applied. */
|
||||
ApplyEntryTemplate(entry_template, ApplyOption_FlushDataCache);
|
||||
/* Finally, apply the changes as directed, flushing the mappings before they're applied (if we should). */
|
||||
ApplyEntryTemplate(entry_template, flush_mapping ? ApplyOption_FlushDataCache : ApplyOption_None);
|
||||
}
|
||||
|
||||
/* We've succeeded, now perform what coalescing we can. */
|
||||
|
||||
@@ -353,12 +353,12 @@ namespace ams::kern::arch::arm64 {
|
||||
l1_entry->IsPrivilegedExecuteNever(),
|
||||
l1_entry->IsContiguous(),
|
||||
!l1_entry->IsGlobal(),
|
||||
static_cast<int>(l1_entry->GetAccessFlag()),
|
||||
static_cast<unsigned int>(l1_entry->GetShareable()),
|
||||
static_cast<int>(l1_entry->GetAccessFlagInteger()),
|
||||
static_cast<unsigned int>(l1_entry->GetShareableInteger()),
|
||||
l1_entry->IsReadOnly(),
|
||||
l1_entry->IsUserAccessible(),
|
||||
l1_entry->IsNonSecure(),
|
||||
static_cast<int>(l1_entry->GetPageAttribute()),
|
||||
static_cast<int>(l1_entry->GetPageAttributeInteger()),
|
||||
l1_entry->IsHeadMergeDisabled(),
|
||||
l1_entry->IsHeadAndBodyMergeDisabled(),
|
||||
l1_entry->IsTailMergeDisabled());
|
||||
@@ -398,12 +398,12 @@ namespace ams::kern::arch::arm64 {
|
||||
l2_entry->IsPrivilegedExecuteNever(),
|
||||
l2_entry->IsContiguous(),
|
||||
!l2_entry->IsGlobal(),
|
||||
static_cast<int>(l2_entry->GetAccessFlag()),
|
||||
static_cast<unsigned int>(l2_entry->GetShareable()),
|
||||
static_cast<int>(l2_entry->GetAccessFlagInteger()),
|
||||
static_cast<unsigned int>(l2_entry->GetShareableInteger()),
|
||||
l2_entry->IsReadOnly(),
|
||||
l2_entry->IsUserAccessible(),
|
||||
l2_entry->IsNonSecure(),
|
||||
static_cast<int>(l2_entry->GetPageAttribute()),
|
||||
static_cast<int>(l2_entry->GetPageAttributeInteger()),
|
||||
l2_entry->IsHeadMergeDisabled(),
|
||||
l2_entry->IsHeadAndBodyMergeDisabled(),
|
||||
l2_entry->IsTailMergeDisabled());
|
||||
@@ -443,12 +443,12 @@ namespace ams::kern::arch::arm64 {
|
||||
l3_entry->IsPrivilegedExecuteNever(),
|
||||
l3_entry->IsContiguous(),
|
||||
!l3_entry->IsGlobal(),
|
||||
static_cast<int>(l3_entry->GetAccessFlag()),
|
||||
static_cast<unsigned int>(l3_entry->GetShareable()),
|
||||
static_cast<int>(l3_entry->GetAccessFlagInteger()),
|
||||
static_cast<unsigned int>(l3_entry->GetShareableInteger()),
|
||||
l3_entry->IsReadOnly(),
|
||||
l3_entry->IsUserAccessible(),
|
||||
l3_entry->IsNonSecure(),
|
||||
static_cast<int>(l3_entry->GetPageAttribute()),
|
||||
static_cast<int>(l3_entry->GetPageAttributeInteger()),
|
||||
l3_entry->IsHeadMergeDisabled(),
|
||||
l3_entry->IsHeadAndBodyMergeDisabled(),
|
||||
l3_entry->IsTailMergeDisabled());
|
||||
|
||||
@@ -26,7 +26,9 @@ namespace ams::kern::arch::arm64 {
|
||||
/* Send KDebug event for this thread's creation. */
|
||||
{
|
||||
KScopedInterruptEnable ei;
|
||||
KDebug::OnDebugEvent(ams::svc::DebugEvent_CreateThread, GetCurrentThread().GetId(), GetInteger(GetCurrentThread().GetThreadLocalRegionAddress()));
|
||||
|
||||
const uintptr_t params[2] = { GetCurrentThread().GetId(), GetInteger(GetCurrentThread().GetThreadLocalRegionAddress()) };
|
||||
KDebug::OnDebugEvent(ams::svc::DebugEvent_CreateThread, params, util::size(params));
|
||||
}
|
||||
|
||||
/* Handle any pending dpc. */
|
||||
@@ -40,8 +42,6 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 El0PsrMask = 0xFF0FFE20;
|
||||
|
||||
ALWAYS_INLINE bool IsFpuEnabled() {
|
||||
return cpu::ArchitecturalFeatureAccessControlRegisterAccessor().IsFpEnabled();
|
||||
}
|
||||
@@ -96,8 +96,8 @@ namespace ams::kern::arch::arm64 {
|
||||
/* SP */
|
||||
/* | */
|
||||
/* v */
|
||||
/* | u64 argument | u64 entrypoint | KThread::StackParameters (size 0x130) | */
|
||||
static_assert(sizeof(KThread::StackParameters) == 0x130);
|
||||
/* | u64 argument | u64 entrypoint | KThread::StackParameters (size 0x140) | */
|
||||
static_assert(sizeof(KThread::StackParameters) == 0x140);
|
||||
|
||||
u64 *stack = GetPointer<u64>(sp);
|
||||
*(--stack) = GetInteger(pc);
|
||||
@@ -191,7 +191,7 @@ namespace ams::kern::arch::arm64 {
|
||||
out->lr = e_ctx->x[30];
|
||||
out->sp = e_ctx->sp;
|
||||
out->pc = e_ctx->pc;
|
||||
out->pstate = e_ctx->psr & El0PsrMask;
|
||||
out->pstate = e_ctx->psr & cpu::El0Aarch64PsrMask;
|
||||
|
||||
/* Get the thread's general purpose registers. */
|
||||
if (thread->IsCallingSvc()) {
|
||||
@@ -227,7 +227,7 @@ namespace ams::kern::arch::arm64 {
|
||||
} else {
|
||||
/* Set special registers. */
|
||||
out->pc = static_cast<u32>(e_ctx->pc);
|
||||
out->pstate = e_ctx->psr & El0PsrMask;
|
||||
out->pstate = e_ctx->psr & cpu::El0Aarch32PsrMask;
|
||||
|
||||
/* Get the thread's general purpose registers. */
|
||||
for (size_t i = 0; i < 15; ++i) {
|
||||
|
||||
@@ -596,8 +596,11 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17ReadIoMemory32BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for read failure. */
|
||||
adr x10, 4f
|
||||
|
||||
1: /* Set our return address so that on read failure we continue as though we read -1. */
|
||||
adr x30, 4f
|
||||
mov x30, x10
|
||||
|
||||
/* Read the word from io. */
|
||||
ldtr w9, [x5]
|
||||
@@ -643,8 +646,11 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17ReadIoMemory16BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for read failure. */
|
||||
adr x10, 4f
|
||||
|
||||
1: /* Set our return address so that on read failure we continue as though we read -1. */
|
||||
adr x30, 4f
|
||||
mov x30, x10
|
||||
|
||||
/* Read the word from io. */
|
||||
ldtrh w9, [x5]
|
||||
@@ -690,8 +696,11 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess16ReadIoMemory8BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for read failure. */
|
||||
adr x10, 4f
|
||||
|
||||
1: /* Set our return address so that on read failure we continue as though we read -1. */
|
||||
adr x30, 4f
|
||||
mov x30, x10
|
||||
|
||||
/* Read the word from io. */
|
||||
ldtrb w9, [x5]
|
||||
@@ -737,11 +746,14 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory32BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for failure. */
|
||||
adr x10, 2f
|
||||
|
||||
1: /* Read the word from normal memory. */
|
||||
ldtr w9, [x5]
|
||||
|
||||
/* Set our return address so that on read failure we continue. */
|
||||
adr x30, 2f
|
||||
/* Set our return address so that on failure we continue. */
|
||||
mov x30, x10
|
||||
|
||||
/* Write the word to io. */
|
||||
sttr w9, [x5]
|
||||
@@ -779,11 +791,14 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess18WriteIoMemory16BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for failure. */
|
||||
adr x10, 2f
|
||||
|
||||
1: /* Read the word from normal memory. */
|
||||
ldtrh w9, [x5]
|
||||
|
||||
/* Set our return address so that on read failure we continue. */
|
||||
adr x30, 2f
|
||||
/* Set our return address so that on failure we continue. */
|
||||
mov x30, x10
|
||||
|
||||
/* Write the word to io. */
|
||||
sttrh w9, [x5]
|
||||
@@ -821,11 +836,14 @@ _ZN3ams4kern4arch5arm6415UserspaceAccess17WriteIoMemory8BitEPvPKvm:
|
||||
/* Save our return address. */
|
||||
mov x8, x30
|
||||
|
||||
/* Prepare return address for failure. */
|
||||
adr x10, 2f
|
||||
|
||||
1: /* Read the word from normal memory. */
|
||||
ldtrb w9, [x5]
|
||||
|
||||
/* Set our return address so that on read failure we continue. */
|
||||
adr x30, 2f
|
||||
/* Set our return address so that on failure we continue. */
|
||||
mov x30, x10
|
||||
|
||||
/* Write the word to io. */
|
||||
sttrb w9, [x5]
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
#include "kern_secure_monitor.hpp"
|
||||
#include "kern_lps_driver.hpp"
|
||||
|
||||
namespace ams::kern::init {
|
||||
|
||||
void StartOtherCore(const ams::kern::init::KInitArguments *init_args);
|
||||
|
||||
}
|
||||
|
||||
namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
namespace {
|
||||
@@ -67,15 +73,10 @@ namespace ams::kern::board::nintendo::nx {
|
||||
constinit KLightLock g_request_lock;
|
||||
constinit KLightLock g_cv_lock;
|
||||
constinit KLightConditionVariable g_cv{util::ConstantInitialize};
|
||||
constinit KPhysicalAddress g_sleep_buffer_phys_addrs[cpu::NumCores];
|
||||
alignas(1_KB) constinit u64 g_sleep_buffers[cpu::NumCores][1_KB / sizeof(u64)];
|
||||
constinit ams::kern::init::KInitArguments g_sleep_init_arguments[cpu::NumCores];
|
||||
constinit SavedSystemRegisters g_sleep_system_registers[cpu::NumCores] = {};
|
||||
|
||||
void PowerOnCpu(int core_id, KPhysicalAddress entry_phys_addr, u64 context_id) {
|
||||
/* Request the secure monitor power on the core. */
|
||||
::ams::kern::arch::arm64::smc::CpuOn<smc::SmcId_Supervisor, true>(cpu::MultiprocessorAffinityRegisterAccessor().GetCpuOnArgument() | core_id, GetInteger(entry_phys_addr), context_id);
|
||||
}
|
||||
|
||||
void WaitOtherCpuPowerOff() {
|
||||
constexpr u64 PmcPhysicalAddress = 0x7000E400;
|
||||
constexpr u32 PWRGATE_STATUS_CE123_MASK = ((1u << 3) - 1) << 9;
|
||||
@@ -473,18 +474,20 @@ namespace ams::kern::board::nintendo::nx {
|
||||
}
|
||||
}
|
||||
|
||||
void KSleepManager::ProcessRequests(uintptr_t buffer) {
|
||||
void KSleepManager::ProcessRequests(uintptr_t sleep_buffer) {
|
||||
const auto target_fw = GetTargetFirmware();
|
||||
const s32 core_id = GetCurrentCoreId();
|
||||
KPhysicalAddress resume_entry_phys_addr = Null<KPhysicalAddress>;
|
||||
|
||||
ams::kern::init::KInitArguments * const init_args = g_sleep_init_arguments + core_id;
|
||||
KPhysicalAddress start_core_phys_addr = Null<KPhysicalAddress>;
|
||||
KPhysicalAddress init_args_phys_addr = Null<KPhysicalAddress>;
|
||||
|
||||
/* Get the physical addresses we'll need. */
|
||||
{
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(g_sleep_buffer_phys_addrs[core_id]), KProcessAddress(buffer)));
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(resume_entry_phys_addr), KProcessAddress(&::ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry)));
|
||||
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(start_core_phys_addr), KProcessAddress(&::ams::kern::init::StartOtherCore)));
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(init_args_phys_addr), KProcessAddress(init_args)));
|
||||
}
|
||||
const KPhysicalAddress sleep_buffer_phys_addr = g_sleep_buffer_phys_addrs[core_id];
|
||||
|
||||
const u64 target_core_mask = (1ul << core_id);
|
||||
|
||||
const bool use_legacy_lps_driver = target_fw < TargetFirmware_2_0_0;
|
||||
@@ -547,15 +550,29 @@ namespace ams::kern::board::nintendo::nx {
|
||||
/* Save the interrupt manager's state. */
|
||||
Kernel::GetInterruptManager().Save(core_id);
|
||||
|
||||
/* Setup the initial arguments. */
|
||||
{
|
||||
init_args->ttbr0 = cpu::GetTtbr0El1();
|
||||
init_args->ttbr1 = cpu::GetTtbr1El1();
|
||||
init_args->tcr = cpu::GetTcrEl1();
|
||||
init_args->mair = cpu::GetMairEl1();
|
||||
init_args->cpuactlr = cpu::GetCpuActlrEl1();
|
||||
init_args->cpuectlr = cpu::GetCpuEctlrEl1();
|
||||
init_args->sctlr = cpu::GetSctlrEl1();
|
||||
init_args->sp = 0;
|
||||
init_args->entrypoint = reinterpret_cast<uintptr_t>(::ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry);
|
||||
init_args->argument = sleep_buffer;
|
||||
}
|
||||
|
||||
/* Ensure that all cores get to this point before continuing. */
|
||||
cpu::SynchronizeAllCores();
|
||||
|
||||
/* Log that the core is going to sleep. */
|
||||
MESOSPHERE_LOG("Core[%d]: Going to sleep, buffer = %010lx\n", core_id, GetInteger(sleep_buffer_phys_addr));
|
||||
MESOSPHERE_LOG("Core[%d]: Going to sleep, buffer = %010lx\n", core_id, sleep_buffer);
|
||||
|
||||
/* If we're on a core other than zero, we can just invoke the sleep handler. */
|
||||
if (core_id != 0) {
|
||||
CpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr));
|
||||
CpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
|
||||
} else {
|
||||
/* Wait for all other cores to be powered off. */
|
||||
WaitOtherCpuPowerOff();
|
||||
@@ -574,9 +591,9 @@ namespace ams::kern::board::nintendo::nx {
|
||||
/* Invoke the sleep handler. */
|
||||
if (!use_legacy_lps_driver) {
|
||||
/* When not using the legacy driver, invoke directly. */
|
||||
CpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr));
|
||||
CpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
|
||||
} else {
|
||||
lps::InvokeCpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr));
|
||||
lps::InvokeCpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
|
||||
}
|
||||
|
||||
/* Restore the debug log state. */
|
||||
@@ -586,8 +603,10 @@ namespace ams::kern::board::nintendo::nx {
|
||||
MESOSPHERE_LOG("Exiting SC7\n");
|
||||
|
||||
/* Wake up the other cores. */
|
||||
cpu::MultiprocessorAffinityRegisterAccessor mpidr;
|
||||
const auto arg = mpidr.GetCpuOnArgument();
|
||||
for (s32 i = 1; i < static_cast<s32>(cpu::NumCores); ++i) {
|
||||
PowerOnCpu(i, resume_entry_phys_addr, GetInteger(g_sleep_buffer_phys_addrs[i]));
|
||||
KSystemControl::Init::TurnOnCpu(arg | i, g_sleep_init_arguments + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,12 @@ namespace ams::kern::board::nintendo::nx {
|
||||
private:
|
||||
static void ResumeEntry(uintptr_t arg);
|
||||
|
||||
static void InvalidateDataCacheForResumeEntry(uintptr_t level);
|
||||
|
||||
static void ProcessRequests(uintptr_t buffer);
|
||||
public:
|
||||
static void Initialize();
|
||||
static void SleepSystem();
|
||||
public:
|
||||
static void CpuSleepHandler(uintptr_t arg, uintptr_t entry);
|
||||
static void CpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_args);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
mov reg, #(((val) >> 0x00) & 0xFFFF); \
|
||||
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl#16
|
||||
|
||||
/* ams::kern::board::nintendo::nx::KSleepManager::CpuSleepHandler(uintptr_t arg, uintptr_t entry) */
|
||||
.section .sleep._ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm, "ax", %progbits
|
||||
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm
|
||||
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm, %function
|
||||
_ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm:
|
||||
/* ams::kern::board::nintendo::nx::KSleepManager::CpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg) */
|
||||
.section .sleep._ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm, "ax", %progbits
|
||||
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm
|
||||
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm, %function
|
||||
_ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm:
|
||||
/* Save arguments. */
|
||||
mov x16, x0
|
||||
mov x17, x1
|
||||
mov x16, x1
|
||||
mov x17, x2
|
||||
|
||||
/* Enable access to FPU registers. */
|
||||
mrs x1, cpacr_el1
|
||||
@@ -74,28 +74,8 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm:
|
||||
stp q28, q29, [x0], #0x20
|
||||
stp q30, q31, [x0], #0x20
|
||||
|
||||
/* Save cpuactlr/cpuectlr. */
|
||||
mrs x1, cpuectlr_el1
|
||||
mrs x2, cpuactlr_el1
|
||||
stp x1, x2, [x0], #0x10
|
||||
|
||||
/* Save ttbr0/ttbr1. */
|
||||
mrs x1, ttbr0_el1
|
||||
mrs x2, ttbr1_el1
|
||||
stp x1, x2, [x0], #0x10
|
||||
|
||||
/* Save tcr/mair. */
|
||||
mrs x1, tcr_el1
|
||||
mrs x2, mair_el1
|
||||
stp x1, x2, [x0], #0x10
|
||||
|
||||
/* Save sctlr/tpidr. */
|
||||
mrs x1, sctlr_el1
|
||||
mrs x2, tpidr_el1
|
||||
stp x1, x2, [x0], #0x10
|
||||
|
||||
/* Save the virtual resumption entrypoint and cntv_cval_el0. */
|
||||
adr x1, 77f
|
||||
/* Save tpidr/cntv_cval_el0. */
|
||||
mrs x1, tpidr_el1
|
||||
mrs x2, cntv_cval_el0
|
||||
stp x1, x2, [x0], #0x10
|
||||
|
||||
@@ -114,8 +94,8 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm:
|
||||
1: /* Suspend. */
|
||||
LOAD_IMMEDIATE_32(x0, 0xC4000001)
|
||||
LOAD_IMMEDIATE_32(x1, 0x0201001B)
|
||||
mov x2, x17
|
||||
mov x3, x16
|
||||
mov x2, x16
|
||||
mov x3, x17
|
||||
smc #1
|
||||
0: b 0b
|
||||
|
||||
@@ -124,65 +104,6 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmm:
|
||||
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm
|
||||
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm, %function
|
||||
_ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm:
|
||||
/* Mask interrupts. */
|
||||
msr daifset, #0xF
|
||||
|
||||
/* Save the argument. */
|
||||
mov x21, x0
|
||||
|
||||
/* Check that we're at the correct exception level. */
|
||||
mrs x0, currentel
|
||||
|
||||
/* Check if we're EL1. */
|
||||
cmp x0, #0x4
|
||||
b.eq 3f
|
||||
|
||||
/* Check if we're EL2. */
|
||||
cmp x0, #0x8
|
||||
b.eq 2f
|
||||
|
||||
1: /* We're running at EL3. */
|
||||
b 1b
|
||||
|
||||
2: /* We're running at EL2. */
|
||||
b 2b
|
||||
|
||||
3: /* We're running at EL1. */
|
||||
|
||||
/* Invalidate the L1 cache. */
|
||||
mov x0, #0
|
||||
bl _ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm
|
||||
|
||||
/* Get the current core id. */
|
||||
mrs x0, mpidr_el1
|
||||
and x0, x0, #0xFF
|
||||
|
||||
/* If we're on core0, we want to invalidate the L2 cache. */
|
||||
cbnz x0, 4f
|
||||
|
||||
mov x0, #1
|
||||
bl _ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm
|
||||
|
||||
4: /* Invalidate the L1 cache. */
|
||||
mov x0, #0
|
||||
bl _ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm
|
||||
|
||||
/* Invalidate the instruction cache. */
|
||||
ic ialluis
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* Invalidate the entire tlb. */
|
||||
tlbi vmalle1is
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* Switch to sp 1. */
|
||||
msr spsel, #1
|
||||
|
||||
/* Prepare to restore the saved context. */
|
||||
mov x0, x21
|
||||
|
||||
/* Enable access to FPU registers. */
|
||||
mrs x1, cpacr_el1
|
||||
orr x1, x1, #0x100000
|
||||
@@ -226,121 +147,12 @@ _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm:
|
||||
ldp q28, q29, [x0], #0x20
|
||||
ldp q30, q31, [x0], #0x20
|
||||
|
||||
/* Restore cpuactlr/cpuectlr. */
|
||||
/* Restore tpidr/cntv_cval_el0. */
|
||||
ldp x1, x2, [x0], #0x10
|
||||
mrs x3, cpuectlr_el1
|
||||
cmp x1, x3
|
||||
5: b.ne 5b
|
||||
mrs x3, cpuactlr_el1
|
||||
cmp x2, x3
|
||||
6: b.ne 6b
|
||||
|
||||
/* Restore ttbr0/ttbr1. */
|
||||
ldp x1, x2, [x0], #0x10
|
||||
msr ttbr0_el1, x1
|
||||
msr ttbr1_el1, x2
|
||||
|
||||
/* Restore tcr/mair. */
|
||||
ldp x1, x2, [x0], #0x10
|
||||
msr tcr_el1, x1
|
||||
msr mair_el1, x2
|
||||
|
||||
/* Get sctlr, tpidr, the entrypoint, and cntv_cval_el0. */
|
||||
ldp x1, x2, [x0], #0x10
|
||||
ldp x3, x4, [x0], #0x10
|
||||
|
||||
/* Set the global context back into x18/tpidr. */
|
||||
msr tpidr_el1, x2
|
||||
msr cntv_cval_el0, x4
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* Restore sctlr with the wxn bit cleared. */
|
||||
bic x2, x1, #0x80000
|
||||
msr sctlr_el1, x2
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* Jump to the entrypoint. */
|
||||
br x3
|
||||
|
||||
77: /* Virtual resumption entrypoint. */
|
||||
|
||||
/* Restore sctlr. */
|
||||
msr sctlr_el1, x1
|
||||
msr tpidr_el1, x1
|
||||
msr cntv_cval_el0, x2
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
ret
|
||||
|
||||
/* ams::kern::board::nintendo::nx::KSleepManager::InvalidateDataCacheForResumeEntry(uintptr_t level) */
|
||||
.section .sleep._ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm, "ax", %progbits
|
||||
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm
|
||||
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm, %function
|
||||
_ZN3ams4kern5board8nintendo2nx13KSleepManager33InvalidateDataCacheForResumeEntryEm:
|
||||
/* cpu::DataSynchronizationBarrier(); */
|
||||
dsb sy
|
||||
|
||||
/* const u64 level_sel_value = level << 1; */
|
||||
lsl x8, x0, #1
|
||||
|
||||
/* cpu::SetCsselrEl1(level_sel_value); */
|
||||
msr csselr_el1, x8
|
||||
|
||||
/* cpu::InstructionMemoryBarrier(); */
|
||||
isb
|
||||
|
||||
/* CacheSizeIdAccessor ccsidr_el1; */
|
||||
mrs x13, ccsidr_el1
|
||||
|
||||
/* const int num_ways = ccsidr_el1.GetAssociativity(); */
|
||||
ubfx w10, w13, #3, #0xA
|
||||
|
||||
/* const int line_size = ccsidr_el1.GetLineSize(); */
|
||||
and w11, w13, #7
|
||||
|
||||
/* const int num_sets = ccsidr_el1.GetNumberOfSets(); */
|
||||
ubfx w13, w13, #0xD, #0xF
|
||||
|
||||
/* int way = 0; */
|
||||
mov w9, wzr
|
||||
|
||||
/* const u64 set_shift = static_cast<u64>(line_size + 4); */
|
||||
add w11, w11, #4
|
||||
|
||||
/* const u64 way_shift = static_cast<u64>(__builtin_clz(num_ways)); */
|
||||
clz w12, w10
|
||||
|
||||
|
||||
0: /* do { */
|
||||
/* int set = 0; */
|
||||
mov w14, wzr
|
||||
|
||||
/* const u64 way_value = (static_cast<u64>(way) << way_shift); */
|
||||
lsl w15, w9, w12
|
||||
|
||||
1: /* do { */
|
||||
|
||||
/* const u64 isw_value = (static_cast<u64>(set) << set_shift) | way_value | level_sel_value; */
|
||||
lsl w16, w14, w11
|
||||
orr w16, w16, w15
|
||||
sxtw x16, w16
|
||||
orr x16, x16, x8
|
||||
|
||||
/* __asm__ __volatile__("dc isw, %0" :: "r"(isw_value) : "memory"); */
|
||||
dc isw, x16
|
||||
|
||||
/* while (set <= num_sets); */
|
||||
cmp w13, w14
|
||||
add w14, w14, #1
|
||||
b.ne 1b
|
||||
|
||||
/* while (way <= num_ways); */
|
||||
cmp w9, w10
|
||||
add w9, w9, #1
|
||||
b.ne 0b
|
||||
|
||||
/* cpu::EnsureInstructionConsistency(); */
|
||||
dsb sy
|
||||
isb
|
||||
/* Return. */
|
||||
ret
|
||||
|
||||
@@ -367,10 +367,14 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||
/* Verify that our minimum is at least as large as Nintendo's. */
|
||||
constexpr size_t MinimumSize = ::ams::svc::RequiredNonSecureSystemMemorySize;
|
||||
static_assert(MinimumSize >= 0x29C8000);
|
||||
constexpr size_t MinimumSizeWithFatal = ::ams::svc::RequiredNonSecureSystemMemorySizeWithFatal;
|
||||
static_assert(MinimumSizeWithFatal >= 0x2C04000);
|
||||
|
||||
return MinimumSize;
|
||||
constexpr size_t MinimumSizeWithoutFatal = ::ams::svc::RequiredNonSecureSystemMemorySize;
|
||||
static_assert(MinimumSizeWithoutFatal >= 0x2A00000);
|
||||
|
||||
/* Include fatal in non-seure size on 16.0.0+. */
|
||||
return kern::GetTargetFirmware() >= ams::TargetFirmware_16_0_0 ? MinimumSizeWithFatal : MinimumSizeWithoutFatal;
|
||||
}
|
||||
|
||||
u8 KSystemControl::Init::GetDebugLogUartPort() {
|
||||
@@ -382,7 +386,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
return static_cast<u8>((value >> 32) & 0xFF);
|
||||
}
|
||||
|
||||
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
void KSystemControl::Init::CpuOnImpl(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<smc::SmcId_Supervisor, false>(core_id, entrypoint, arg)) == 0);
|
||||
}
|
||||
|
||||
@@ -398,40 +402,41 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* System Initialization. */
|
||||
void KSystemControl::InitializePhase1() {
|
||||
/* Initialize our random generator. */
|
||||
/* Configure KTargetSystem. */
|
||||
{
|
||||
/* Set IsDebugMode. */
|
||||
{
|
||||
KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode));
|
||||
|
||||
/* If debug mode, we want to initialize uart logging. */
|
||||
KTargetSystem::EnableDebugLogging(KTargetSystem::IsDebugMode());
|
||||
}
|
||||
|
||||
/* Set Kernel Configuration. */
|
||||
{
|
||||
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
|
||||
|
||||
KTargetSystem::EnableDebugMemoryFill(kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>());
|
||||
KTargetSystem::EnableUserExceptionHandlers(kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>());
|
||||
KTargetSystem::EnableDynamicResourceLimits(!kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>());
|
||||
KTargetSystem::EnableUserPmuAccess(kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>());
|
||||
|
||||
g_call_smc_on_panic = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>();
|
||||
}
|
||||
|
||||
/* Set Kernel Debugging. */
|
||||
{
|
||||
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
|
||||
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
|
||||
KTargetSystem::EnableKernelDebugging(GetConfigBool(smc::ConfigItem::DisableProgramVerification));
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize random and resource limit. */
|
||||
{
|
||||
u64 seed;
|
||||
smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed));
|
||||
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||
s_initialized_random_generator = true;
|
||||
}
|
||||
|
||||
/* Set IsDebugMode. */
|
||||
{
|
||||
KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode));
|
||||
|
||||
/* If debug mode, we want to initialize uart logging. */
|
||||
KTargetSystem::EnableDebugLogging(KTargetSystem::IsDebugMode());
|
||||
KDebugLog::Initialize();
|
||||
}
|
||||
|
||||
/* Set Kernel Configuration. */
|
||||
{
|
||||
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
|
||||
|
||||
KTargetSystem::EnableDebugMemoryFill(kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>());
|
||||
KTargetSystem::EnableUserExceptionHandlers(kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>());
|
||||
KTargetSystem::EnableDynamicResourceLimits(!kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>());
|
||||
KTargetSystem::EnableUserPmuAccess(kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>());
|
||||
|
||||
g_call_smc_on_panic = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>();
|
||||
}
|
||||
|
||||
/* Set Kernel Debugging. */
|
||||
{
|
||||
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
|
||||
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
|
||||
KTargetSystem::EnableKernelDebugging(GetConfigBool(smc::ConfigItem::DisableProgramVerification));
|
||||
KSystemControlBase::InitializePhase1Base(seed);
|
||||
}
|
||||
|
||||
/* Configure the Kernel Carveout region. */
|
||||
@@ -441,9 +446,6 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize());
|
||||
}
|
||||
|
||||
/* Initialize the system resource limit (and potentially other things). */
|
||||
KSystemControlBase::InitializePhase1(true);
|
||||
}
|
||||
|
||||
void KSystemControl::InitializePhase2() {
|
||||
|
||||
@@ -395,7 +395,7 @@ namespace ams::kern::board::nintendo::nx::lps {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry) {
|
||||
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg) {
|
||||
/* Verify that we're allowed to perform suspension. */
|
||||
MESOSPHERE_ABORT_UNLESS(g_lps_init_done);
|
||||
MESOSPHERE_ABORT_UNLESS(GetCurrentCoreId() == 0);
|
||||
@@ -416,7 +416,7 @@ namespace ams::kern::board::nintendo::nx::lps {
|
||||
Read(g_pmc_address + APBDEV_PMC_SCRATCH0);
|
||||
|
||||
/* Invoke the sleep hander. */
|
||||
KSleepManager::CpuSleepHandler(arg, entry);
|
||||
KSleepManager::CpuSleepHandler(arg, entry, entry_arg);
|
||||
|
||||
/* Disable deep power down. */
|
||||
Write(g_pmc_address + APBDEV_PMC_DPD_ENABLE, 0);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
void Initialize();
|
||||
Result EnableSuspend(bool enable);
|
||||
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry);
|
||||
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg);
|
||||
void ResumeBpmpFirmware();
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace ams::kern::init {
|
||||
|
||||
/* For macro convenience. */
|
||||
using KSessionRequestMappings = KSessionRequest::SessionMappings::DynamicMappings;
|
||||
using KThreadLockInfo = KThread::LockWithPriorityInheritanceInfo;
|
||||
|
||||
#define SLAB_COUNT(CLASS) g_slab_resource_counts.num_##CLASS
|
||||
|
||||
@@ -43,8 +44,9 @@ namespace ams::kern::init {
|
||||
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
|
||||
HANDLER(KIoPool, (SLAB_COUNT(KIoPool)), ## __VA_ARGS__) \
|
||||
HANDLER(KIoRegion, (SLAB_COUNT(KIoRegion)), ## __VA_ARGS__) \
|
||||
HANDLER(KSessionRequestMappings, (SLAB_COUNT(KSessionRequestMappings)), ## __VA_ARGS__) \
|
||||
HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \
|
||||
HANDLER(KSessionRequestMappings, (SLAB_COUNT(KSessionRequestMappings)), ## __VA_ARGS__)
|
||||
HANDLER(KThreadLockInfo, (SLAB_COUNT(KThread)), ## __VA_ARGS__)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -140,7 +142,7 @@ namespace ams::kern::init {
|
||||
}
|
||||
|
||||
size_t CalculateSlabHeapGapSize() {
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MB - 320_KB;
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MB - 356_KB;
|
||||
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
|
||||
return KernelSlabHeapGapSize;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool DecrementIfLessThan(s32 *out, KProcessAddress address, s32 value) {
|
||||
KScopedInterruptDisable di;
|
||||
/* NOTE: If scheduler lock is not held here, interrupt disable is required. */
|
||||
/* KScopedInterruptDisable di; */
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
if (!cpu::CanAccessAtomic(address)) {
|
||||
return false;
|
||||
@@ -34,7 +36,9 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool UpdateIfEqual(s32 *out, KProcessAddress address, s32 value, s32 new_value) {
|
||||
KScopedInterruptDisable di;
|
||||
/* NOTE: If scheduler lock is not held here, interrupt disable is required. */
|
||||
/* KScopedInterruptDisable di; */
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
if (!cpu::CanAccessAtomic(address)) {
|
||||
return false;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ams::kern {
|
||||
|
||||
constexpr uintptr_t Invalid = std::numeric_limits<uintptr_t>::max();
|
||||
|
||||
constexpr KAddressSpaceInfo AddressSpaceInfos[] = {
|
||||
constinit KAddressSpaceInfo AddressSpaceInfos[] = {
|
||||
{ 32, ams::svc::AddressSmallMap32Start, ams::svc::AddressSmallMap32Size, KAddressSpaceInfo::Type_MapSmall, },
|
||||
{ 32, ams::svc::AddressLargeMap32Start, ams::svc::AddressLargeMap32Size, KAddressSpaceInfo::Type_MapLarge, },
|
||||
{ 32, Invalid, ams::svc::AddressMemoryRegionHeap32Size, KAddressSpaceInfo::Type_Heap, },
|
||||
@@ -37,67 +37,27 @@ namespace ams::kern {
|
||||
{ 39, Invalid, ams::svc::AddressMemoryRegionStack39Size, KAddressSpaceInfo::Type_Stack, },
|
||||
};
|
||||
|
||||
constexpr bool IsAllowedIndexForAddress(size_t index) {
|
||||
return index < util::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid;
|
||||
}
|
||||
|
||||
constexpr size_t AddressSpaceIndices32Bit[KAddressSpaceInfo::Type_Count] = {
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
|
||||
constexpr size_t AddressSpaceIndices36Bit[KAddressSpaceInfo::Type_Count] = {
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
|
||||
constexpr size_t AddressSpaceIndices39Bit[KAddressSpaceInfo::Type_Count] = {
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
|
||||
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_Map39Bit && type != KAddressSpaceInfo::Type_Stack;
|
||||
}
|
||||
|
||||
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_Map39Bit && type != KAddressSpaceInfo::Type_Stack;
|
||||
}
|
||||
|
||||
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_MapLarge;
|
||||
KAddressSpaceInfo &GetAddressSpaceInfo(size_t width, KAddressSpaceInfo::Type type) {
|
||||
for (auto &info : AddressSpaceInfos) {
|
||||
if (info.GetWidth() == width && info.GetType() == type) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
MESOSPHERE_PANIC("Could not find AddressSpaceInfo");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uintptr_t KAddressSpaceInfo::GetAddressSpaceStart(size_t width, KAddressSpaceInfo::Type type) {
|
||||
switch (width) {
|
||||
case 32:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed32BitType(type));
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[type]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[type]].GetAddress();
|
||||
case 36:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed36BitType(type));
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[type]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[type]].GetAddress();
|
||||
case 39:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed39BitType(type));
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[type]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[type]].GetAddress();
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
return GetAddressSpaceInfo(width, type).GetAddress();
|
||||
}
|
||||
|
||||
size_t KAddressSpaceInfo::GetAddressSpaceSize(size_t width, KAddressSpaceInfo::Type type) {
|
||||
switch (width) {
|
||||
case 32:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed32BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[type]].GetSize();
|
||||
case 36:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed36BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[type]].GetSize();
|
||||
case 39:
|
||||
MESOSPHERE_ABORT_UNLESS(IsAllowed39BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[type]].GetSize();
|
||||
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
return GetAddressSpaceInfo(width, type).GetSize();
|
||||
}
|
||||
|
||||
void KAddressSpaceInfo::SetAddressSpaceSize(size_t width, Type type, size_t size) {
|
||||
GetAddressSpaceInfo(width, type).SetSize(size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,34 +77,35 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Remove waiter thread. */
|
||||
s32 num_waiters;
|
||||
KThread *next_owner_thread = owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
|
||||
bool has_waiters;
|
||||
KThread * const next_owner_thread = owner_thread->RemoveWaiterByKey(std::addressof(has_waiters), addr);
|
||||
|
||||
/* Determine the next tag. */
|
||||
u32 next_value = 0;
|
||||
if (next_owner_thread != nullptr) {
|
||||
next_value = next_owner_thread->GetAddressKeyValue();
|
||||
if (num_waiters > 1) {
|
||||
if (has_waiters) {
|
||||
next_value |= ams::svc::HandleWaitMask;
|
||||
}
|
||||
|
||||
/* Write the value to userspace. */
|
||||
Result result;
|
||||
if (AMS_LIKELY(WriteToUser(addr, std::addressof(next_value)))) {
|
||||
result = ResultSuccess();
|
||||
} else {
|
||||
result = svc::ResultInvalidCurrentMemory();
|
||||
}
|
||||
|
||||
/* Signal the next owner thread. */
|
||||
next_owner_thread->EndWait(result);
|
||||
R_RETURN(result);
|
||||
} else {
|
||||
/* Just write the value to userspace. */
|
||||
R_UNLESS(WriteToUser(addr, std::addressof(next_value)), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Synchronize memory before proceeding. */
|
||||
cpu::DataMemoryBarrierInnerShareable();
|
||||
|
||||
/* Write the value to userspace. */
|
||||
Result result;
|
||||
if (AMS_LIKELY(WriteToUser(addr, std::addressof(next_value)))) {
|
||||
result = ResultSuccess();
|
||||
} else {
|
||||
result = svc::ResultInvalidCurrentMemory();
|
||||
}
|
||||
|
||||
/* If necessary, signal the next owner thread. */
|
||||
if (next_owner_thread != nullptr) {
|
||||
next_owner_thread->EndWait(result);
|
||||
}
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +158,8 @@ namespace ams::kern {
|
||||
u32 prev_tag;
|
||||
bool can_access;
|
||||
{
|
||||
KScopedInterruptDisable di;
|
||||
/* NOTE: If scheduler lock is not held here, interrupt disable is required. */
|
||||
/* KScopedInterruptDisable di; */
|
||||
|
||||
can_access = cpu::CanAccessAtomic(address);
|
||||
if (AMS_LIKELY(can_access)) {
|
||||
@@ -198,9 +200,11 @@ namespace ams::kern {
|
||||
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) {
|
||||
KThread *target_thread = std::addressof(*it);
|
||||
|
||||
this->SignalImpl(target_thread);
|
||||
it = m_tree.erase(it);
|
||||
target_thread->ClearConditionVariable();
|
||||
|
||||
this->SignalImpl(target_thread);
|
||||
|
||||
++num_waiters;
|
||||
}
|
||||
|
||||
@@ -230,15 +234,15 @@ namespace ams::kern {
|
||||
/* Update the value and process for the next owner. */
|
||||
{
|
||||
/* Remove waiter thread. */
|
||||
s32 num_waiters;
|
||||
KThread *next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), GetInteger(addr));
|
||||
bool has_waiters;
|
||||
KThread *next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(has_waiters), GetInteger(addr));
|
||||
|
||||
/* Update for the next owner thread. */
|
||||
u32 next_value = 0;
|
||||
if (next_owner_thread != nullptr) {
|
||||
/* Get the next tag value. */
|
||||
next_value = next_owner_thread->GetAddressKeyValue();
|
||||
if (num_waiters > 1) {
|
||||
if (has_waiters) {
|
||||
next_value |= ams::svc::HandleWaitMask;
|
||||
}
|
||||
|
||||
|
||||
@@ -219,12 +219,21 @@ namespace ams::kern {
|
||||
const s32 core_id = GetCurrentCoreId();
|
||||
KThread *thread = process->GetRunningThread(core_id);
|
||||
|
||||
/* Check that the thread's idle count is correct. */
|
||||
R_UNLESS(process->GetRunningThreadIdleCount(core_id) == Kernel::GetScheduler(core_id).GetIdleCount(), svc::ResultNoThread());
|
||||
|
||||
/* Check that the thread is running on the current core. */
|
||||
R_UNLESS(thread != nullptr, svc::ResultUnknownThread());
|
||||
R_UNLESS(thread->GetActiveCore() == core_id, svc::ResultUnknownThread());
|
||||
/* We want to check that the thread is actually running. */
|
||||
/* If it is, then the scheduler will have just switched from the thread to the current thread. */
|
||||
/* This implies exactly one switch will have taken place, and the current thread will be on the current core. */
|
||||
const auto &scheduler = Kernel::GetScheduler(core_id);
|
||||
if (!(thread != nullptr && thread->GetActiveCore() == core_id && process->GetRunningThreadSwitchCount(core_id) + 1 == scheduler.GetSwitchCount())) {
|
||||
/* The most recent thread switch was from a thread other than the expected one to the current one. */
|
||||
/* We want to use the appropriate result to inform userland about what thread we switched from. */
|
||||
if (scheduler.GetIdleCount() + 1 == scheduler.GetSwitchCount()) {
|
||||
/* We switched from the idle thread. */
|
||||
R_THROW(svc::ResultNoThread());
|
||||
} else {
|
||||
/* We switched from some other unknown thread. */
|
||||
R_THROW(svc::ResultUnknownThread());
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the thread's exception context. */
|
||||
GetExceptionContext(thread)->GetSvcThreadContext(out_context);
|
||||
@@ -280,7 +289,7 @@ namespace ams::kern {
|
||||
m_old_process_state = target->SetDebugObject(this);
|
||||
|
||||
/* Send an event for our attaching to the process. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_CreateProcess);
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_CreateProcess, nullptr, 0);
|
||||
|
||||
/* Send events for attaching to each thread in the process. */
|
||||
{
|
||||
@@ -295,7 +304,8 @@ namespace ams::kern {
|
||||
it->SetDebugAttached();
|
||||
|
||||
/* Send the event. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_CreateThread, it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()));
|
||||
const uintptr_t params[2] = { it->GetId(), GetInteger(it->GetThreadLocalRegionAddress()) };
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_CreateThread, params, util::size(params));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,7 +316,8 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* Send an exception event to represent our attaching. */
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, ams::svc::DebugException_DebuggerAttached);
|
||||
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::DebugException_DebuggerAttached) };
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, params, util::size(params));
|
||||
|
||||
/* Signal. */
|
||||
this->NotifyAvailable();
|
||||
@@ -344,22 +355,22 @@ namespace ams::kern {
|
||||
/* Get the currently active threads. */
|
||||
constexpr u64 ThreadIdNoThread = -1ll;
|
||||
constexpr u64 ThreadIdUnknownThread = -2ll;
|
||||
u64 thread_ids[cpu::NumCores];
|
||||
for (size_t i = 0; i < util::size(thread_ids); ++i) {
|
||||
uintptr_t debug_info_params[1 + cpu::NumCores] = { static_cast<uintptr_t>(ams::svc::DebugException_DebuggerBreak), };
|
||||
for (size_t i = 0; i < cpu::NumCores; ++i) {
|
||||
/* Get the currently running thread. */
|
||||
KThread *thread = target->GetRunningThread(i);
|
||||
|
||||
/* Check that the thread's idle count is correct. */
|
||||
if (target->GetRunningThreadIdleCount(i) == Kernel::GetScheduler(i).GetIdleCount()) {
|
||||
if (thread != nullptr && static_cast<size_t>(thread->GetActiveCore()) == i) {
|
||||
thread_ids[i] = thread->GetId();
|
||||
debug_info_params[1 + i] = thread->GetId();
|
||||
} else {
|
||||
/* We found an unknown thread. */
|
||||
thread_ids[i] = ThreadIdUnknownThread;
|
||||
debug_info_params[1 + i] = ThreadIdUnknownThread;
|
||||
}
|
||||
} else {
|
||||
/* We didn't find a thread. */
|
||||
thread_ids[i] = ThreadIdNoThread;
|
||||
debug_info_params[1 + i] = ThreadIdNoThread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,11 +384,7 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* Send an exception event to represent our breaking the process. */
|
||||
/* 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)>());
|
||||
this->PushDebugEvent(ams::svc::DebugEvent_Exception, debug_info_params, util::size(debug_info_params));
|
||||
|
||||
/* Signal. */
|
||||
this->NotifyAvailable();
|
||||
@@ -725,7 +732,7 @@ namespace ams::kern {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KEventInfo *KDebugBase::CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 cur_thread_id) {
|
||||
KEventInfo *KDebugBase::CreateDebugEvent(ams::svc::DebugEvent event, u64 cur_thread_id, const uintptr_t *params, size_t num_params) {
|
||||
/* Allocate a new event. */
|
||||
KEventInfo *info = KEventInfo::Allocate();
|
||||
|
||||
@@ -740,23 +747,33 @@ namespace ams::kern {
|
||||
switch (event) {
|
||||
case ams::svc::DebugEvent_CreateProcess:
|
||||
{
|
||||
/* ... */
|
||||
/* Check parameters. */
|
||||
MESOSPHERE_ASSERT(params == nullptr);
|
||||
MESOSPHERE_ASSERT(num_params == 0);
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_CreateThread:
|
||||
{
|
||||
/* Check parameters. */
|
||||
MESOSPHERE_ASSERT(params != nullptr);
|
||||
MESOSPHERE_ASSERT(num_params == 2);
|
||||
|
||||
/* Set the thread id. */
|
||||
info->thread_id = param0;
|
||||
info->thread_id = params[0];
|
||||
|
||||
/* Set the thread creation info. */
|
||||
info->info.create_thread.thread_id = param0;
|
||||
info->info.create_thread.tls_address = param1;
|
||||
info->info.create_thread.thread_id = params[0];
|
||||
info->info.create_thread.tls_address = params[1];
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitProcess:
|
||||
{
|
||||
/* Check parameters. */
|
||||
MESOSPHERE_ASSERT(params != nullptr);
|
||||
MESOSPHERE_ASSERT(num_params == 1);
|
||||
|
||||
/* Set the exit reason. */
|
||||
info->info.exit_process.reason = static_cast<ams::svc::ProcessExitReason>(param0);
|
||||
info->info.exit_process.reason = static_cast<ams::svc::ProcessExitReason>(params[0]);
|
||||
|
||||
/* Clear the thread id and flags. */
|
||||
info->thread_id = 0;
|
||||
@@ -765,30 +782,40 @@ namespace ams::kern {
|
||||
break;
|
||||
case ams::svc::DebugEvent_ExitThread:
|
||||
{
|
||||
/* Check parameters. */
|
||||
MESOSPHERE_ASSERT(params != nullptr);
|
||||
MESOSPHERE_ASSERT(num_params == 2);
|
||||
|
||||
/* Set the thread id. */
|
||||
info->thread_id = param0;
|
||||
info->thread_id = params[0];
|
||||
|
||||
/* Set the exit reason. */
|
||||
info->info.exit_thread.reason = static_cast<ams::svc::ThreadExitReason>(param1);
|
||||
info->info.exit_thread.reason = static_cast<ams::svc::ThreadExitReason>(params[1]);
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugEvent_Exception:
|
||||
{
|
||||
/* Check parameters. */
|
||||
MESOSPHERE_ASSERT(params != nullptr);
|
||||
MESOSPHERE_ASSERT(num_params >= 1);
|
||||
|
||||
/* Set the thread id. */
|
||||
info->thread_id = cur_thread_id;
|
||||
|
||||
/* Set the exception type, and clear the count. */
|
||||
info->info.exception.exception_type = static_cast<ams::svc::DebugException>(param0);
|
||||
info->info.exception.exception_type = static_cast<ams::svc::DebugException>(params[0]);
|
||||
info->info.exception.exception_data_count = 0;
|
||||
switch (static_cast<ams::svc::DebugException>(param0)) {
|
||||
switch (static_cast<ams::svc::DebugException>(params[0])) {
|
||||
case ams::svc::DebugException_UndefinedInstruction:
|
||||
case ams::svc::DebugException_BreakPoint:
|
||||
case ams::svc::DebugException_UndefinedSystemCall:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
MESOSPHERE_ASSERT(num_params >= 3);
|
||||
|
||||
info->info.exception.exception_address = params[1];
|
||||
|
||||
info->info.exception.exception_data_count = 1;
|
||||
info->info.exception.exception_data[0] = param2;
|
||||
info->info.exception.exception_data[0] = params[2];
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerAttached:
|
||||
@@ -800,12 +827,14 @@ namespace ams::kern {
|
||||
break;
|
||||
case ams::svc::DebugException_UserBreak:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
MESOSPHERE_ASSERT(num_params >= 2);
|
||||
|
||||
info->info.exception.exception_data_count = 3;
|
||||
info->info.exception.exception_data[0] = param2;
|
||||
info->info.exception.exception_data[1] = param3;
|
||||
info->info.exception.exception_data[2] = param4;
|
||||
info->info.exception.exception_address = params[1];
|
||||
|
||||
info->info.exception.exception_data_count = 0;
|
||||
for (size_t i = 2; i < num_params; ++i) {
|
||||
info->info.exception.exception_data[info->info.exception.exception_data_count++] = params[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerBreak:
|
||||
@@ -814,11 +843,10 @@ namespace ams::kern {
|
||||
|
||||
info->info.exception.exception_address = 0;
|
||||
|
||||
info->info.exception.exception_data_count = 4;
|
||||
info->info.exception.exception_data[0] = param1;
|
||||
info->info.exception.exception_data[1] = param2;
|
||||
info->info.exception.exception_data[2] = param3;
|
||||
info->info.exception.exception_data[3] = param4;
|
||||
info->info.exception.exception_data_count = 0;
|
||||
for (size_t i = 1; i < num_params; ++i) {
|
||||
info->info.exception.exception_data[info->info.exception.exception_data_count++] = params[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ams::svc::DebugException_MemorySystemError:
|
||||
@@ -831,7 +859,9 @@ namespace ams::kern {
|
||||
case ams::svc::DebugException_AlignmentFault:
|
||||
default:
|
||||
{
|
||||
info->info.exception.exception_address = param1;
|
||||
MESOSPHERE_ASSERT(num_params >= 2);
|
||||
|
||||
info->info.exception.exception_address = params[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -843,9 +873,9 @@ namespace ams::kern {
|
||||
return info;
|
||||
}
|
||||
|
||||
void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
|
||||
void KDebugBase::PushDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
|
||||
/* Create and enqueue and event. */
|
||||
if (KEventInfo *new_info = CreateDebugEvent(event, param0, param1, param2, param3, param4, GetCurrentThread().GetId()); new_info != nullptr) {
|
||||
if (KEventInfo *new_info = CreateDebugEvent(event, GetCurrentThread().GetId(), params, num_params); new_info != nullptr) {
|
||||
this->EnqueueDebugEventInfo(new_info);
|
||||
}
|
||||
}
|
||||
@@ -952,7 +982,10 @@ namespace ams::kern {
|
||||
break;
|
||||
case ams::svc::DebugException_DebuggerBreak:
|
||||
{
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == 4);
|
||||
/* TODO: How does this work with non-4 cpu count? */
|
||||
static_assert(cpu::NumCores <= 4);
|
||||
|
||||
MESOSPHERE_ASSERT(info->info.exception.exception_data_count == cpu::NumCores);
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[0] = info->info.exception.exception_data[0];
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[1] = info->info.exception.exception_data[1];
|
||||
out->info.exception.specific.debugger_break.active_thread_ids[2] = info->info.exception.exception_data[2];
|
||||
@@ -1066,7 +1099,7 @@ namespace ams::kern {
|
||||
return !empty || !m_is_attached || this->GetProcessUnsafe()->IsTerminated();
|
||||
}
|
||||
|
||||
Result KDebugBase::ProcessDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
|
||||
Result KDebugBase::ProcessDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
|
||||
/* Get the current process. */
|
||||
KProcess *process = GetCurrentProcessPointer();
|
||||
|
||||
@@ -1108,7 +1141,7 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* Push the event. */
|
||||
debug->PushDebugEvent(event, param0, param1, param2, param3, param4);
|
||||
debug->PushDebugEvent(event, params, num_params);
|
||||
debug->NotifyAvailable();
|
||||
|
||||
/* Set the process as breaked. */
|
||||
@@ -1144,9 +1177,9 @@ namespace ams::kern {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KDebugBase::OnDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4) {
|
||||
Result KDebugBase::OnDebugEvent(ams::svc::DebugEvent event, const uintptr_t *params, size_t num_params) {
|
||||
if (KProcess *process = GetCurrentProcessPointer(); process != nullptr && process->IsAttachedToDebugger()) {
|
||||
R_RETURN(ProcessDebugEvent(event, param0, param1, param2, param3, param4));
|
||||
R_RETURN(ProcessDebugEvent(event, params, num_params));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -1161,7 +1194,8 @@ namespace ams::kern {
|
||||
|
||||
/* Push the event. */
|
||||
if (KDebugBase *debug = GetDebugObject(process); debug != nullptr) {
|
||||
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, ams::svc::ProcessExitReason_ExitProcess);
|
||||
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::ProcessExitReason_ExitProcess) };
|
||||
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, params, util::size(params));
|
||||
debug->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
@@ -1179,7 +1213,8 @@ namespace ams::kern {
|
||||
|
||||
/* Push the event. */
|
||||
if (KDebugBase *debug = GetDebugObject(process); debug != nullptr) {
|
||||
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, ams::svc::ProcessExitReason_TerminateProcess);
|
||||
const uintptr_t params[1] = { static_cast<uintptr_t>(ams::svc::ProcessExitReason_TerminateProcess) };
|
||||
debug->PushDebugEvent(ams::svc::DebugEvent_ExitProcess, params, util::size(params));
|
||||
debug->NotifyAvailable();
|
||||
}
|
||||
}
|
||||
@@ -1193,7 +1228,8 @@ namespace ams::kern {
|
||||
/* Check if we're attached to a debugger. */
|
||||
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr && process->IsAttachedToDebugger()) {
|
||||
/* If we are, submit the event. */
|
||||
R_TRY(OnDebugEvent(ams::svc::DebugEvent_ExitThread, thread->GetId(), thread->IsTerminationRequested() ? ams::svc::ThreadExitReason_TerminateThread : ams::svc::ThreadExitReason_ExitThread));
|
||||
const uintptr_t params[2] = { thread->GetId(), static_cast<uintptr_t>(thread->IsTerminationRequested() ? ams::svc::ThreadExitReason_TerminateThread : ams::svc::ThreadExitReason_ExitThread) };
|
||||
R_TRY(OnDebugEvent(ams::svc::DebugEvent_ExitThread, params, util::size(params)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace ams::kern {
|
||||
|
||||
/* Check if the address/size falls within any allowable extents. */
|
||||
for (const auto &extents : g_io_region_extents) {
|
||||
if (extents.address <= address && address + size - 1 <= extents.address + extents.size - 1) {
|
||||
if (extents.size != 0 && extents.address <= address && address + size - 1 <= extents.address + extents.size - 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -106,12 +106,23 @@ namespace ams::kern {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
/* Check that the desired range isn't already in our pool. */
|
||||
for (const auto ®ion : m_io_region_list) {
|
||||
R_UNLESS(!region.Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
|
||||
{
|
||||
/* Get the lowest region with address >= the new region that's already in our tree. */
|
||||
auto lowest_after = m_io_region_tree.nfind_key(new_region->GetAddress());
|
||||
if (lowest_after != m_io_region_tree.end()) {
|
||||
R_UNLESS(!lowest_after->Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
|
||||
}
|
||||
|
||||
/* There is no region with address >= the new region already in our tree, but we also need to check */
|
||||
/* for a region with address < the new region already in our tree. */
|
||||
if (lowest_after != m_io_region_tree.begin()) {
|
||||
auto highest_before = --lowest_after;
|
||||
R_UNLESS(!highest_before->Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the region to our pool. */
|
||||
m_io_region_list.push_back(*new_region);
|
||||
m_io_region_tree.insert(*new_region);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -122,8 +133,8 @@ namespace ams::kern {
|
||||
/* Lock ourselves. */
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
/* Remove the region from our list. */
|
||||
m_io_region_list.erase(m_io_region_list.iterator_to(*region));
|
||||
/* Remove the region from our tree. */
|
||||
m_io_region_tree.erase(m_io_region_tree.iterator_to(*region));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace ams::kern {
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
/* Unmap ourselves. */
|
||||
R_TRY(GetCurrentProcess().GetPageTable().UnmapIoRegion(address, m_physical_address, size));
|
||||
R_TRY(GetCurrentProcess().GetPageTable().UnmapIoRegion(address, m_physical_address, size, m_mapping));
|
||||
|
||||
/* Remove ourselves from the current process. */
|
||||
GetCurrentProcess().RemoveIoRegion(this);
|
||||
|
||||
@@ -68,13 +68,13 @@ namespace ams::kern {
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Get the next owner. */
|
||||
s32 num_waiters;
|
||||
KThread *next_owner = owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_tag)));
|
||||
bool has_waiters;
|
||||
KThread *next_owner = owner_thread->RemoveWaiterByKey(std::addressof(has_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_tag)));
|
||||
|
||||
/* Pass the lock to the next owner. */
|
||||
uintptr_t next_tag = 0;
|
||||
if (next_owner != nullptr) {
|
||||
next_tag = reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
|
||||
next_tag = reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(has_waiters);
|
||||
|
||||
next_owner->EndWait(ResultSuccess());
|
||||
|
||||
|
||||
@@ -1600,7 +1600,7 @@ namespace ams::kern {
|
||||
|
||||
/* Perform mapping operation. */
|
||||
const KPageProperties properties = { new_perm, false, false, DisableMergeAttribute_None };
|
||||
const auto operation = was_x ? OperationType_ChangePermissionsAndRefresh : OperationType_ChangePermissions;
|
||||
const auto operation = was_x ? OperationType_ChangePermissionsAndRefreshAndFlush : OperationType_ChangePermissions;
|
||||
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null<KPhysicalAddress>, false, properties, operation, false));
|
||||
|
||||
/* Update the blocks. */
|
||||
@@ -1649,7 +1649,7 @@ namespace ams::kern {
|
||||
|
||||
/* Perform operation. */
|
||||
const KPageProperties properties = { old_perm, false, (new_attr & KMemoryAttribute_Uncached) != 0, DisableMergeAttribute_None };
|
||||
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissionsAndRefresh, false));
|
||||
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissionsAndRefreshAndFlush, false));
|
||||
|
||||
/* Update the blocks. */
|
||||
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None);
|
||||
@@ -2019,15 +2019,22 @@ namespace ams::kern {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size) {
|
||||
Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, ams::svc::MemoryMapping mapping) {
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
/* Lock the table. */
|
||||
KScopedLightLock lk(m_general_lock);
|
||||
|
||||
/* Validate the memory state. */
|
||||
KMemoryState old_state;
|
||||
KMemoryPermission old_perm;
|
||||
KMemoryAttribute old_attr;
|
||||
size_t num_allocator_blocks;
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), dst_address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_Locked));
|
||||
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||
dst_address, size,
|
||||
KMemoryState_All, KMemoryState_Io,
|
||||
KMemoryPermission_None, KMemoryPermission_None,
|
||||
KMemoryAttribute_All, KMemoryAttribute_Locked));
|
||||
|
||||
/* Validate that the region being unmapped corresponds to the physical range described. */
|
||||
{
|
||||
@@ -2060,9 +2067,23 @@ namespace ams::kern {
|
||||
/* We're going to perform an update, so create a helper. */
|
||||
KScopedPageTableUpdater updater(this);
|
||||
|
||||
/* If the region being unmapped is Memory, synchronize. */
|
||||
if (mapping == ams::svc::MemoryMapping_Memory) {
|
||||
/* Change the region to be uncached. */
|
||||
const KPageProperties properties = { old_perm, false, true, DisableMergeAttribute_None };
|
||||
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), dst_address, num_pages, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissionsAndRefresh, false));
|
||||
|
||||
/* Temporarily unlock ourselves, so that other operations can occur while we flush the region. */
|
||||
m_general_lock.Unlock();
|
||||
ON_SCOPE_EXIT { m_general_lock.Lock(); };
|
||||
|
||||
/* Flush the region. */
|
||||
MESOSPHERE_R_ABORT_UNLESS(cpu::FlushDataCache(GetVoidPointer(dst_address), size));
|
||||
}
|
||||
|
||||
/* Perform the unmap. */
|
||||
const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None };
|
||||
R_TRY(this->Operate(updater.GetPageList(), dst_address, num_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, false));
|
||||
MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), dst_address, num_pages, Null<KPhysicalAddress>, false, unmap_properties, OperationType_Unmap, false));
|
||||
|
||||
/* Update the blocks. */
|
||||
m_memory_block_manager.Update(std::addressof(allocator), dst_address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal);
|
||||
|
||||
@@ -198,7 +198,6 @@ namespace ams::kern {
|
||||
/* Set misc fields. */
|
||||
m_state = State_Created;
|
||||
m_main_thread_stack_size = 0;
|
||||
m_creation_time = KHardwareTimer::GetTick();
|
||||
m_used_kernel_memory_size = 0;
|
||||
m_ideal_core_id = 0;
|
||||
m_flags = params.flags;
|
||||
@@ -209,11 +208,16 @@ namespace ams::kern {
|
||||
m_is_application = (params.flags & ams::svc::CreateProcessFlag_IsApplication);
|
||||
m_is_jit_debug = false;
|
||||
|
||||
#if defined(MESOSPHERE_ENABLE_PROCESS_CREATION_TIME)
|
||||
m_creation_time = KHardwareTimer::GetTick();
|
||||
#endif
|
||||
|
||||
/* Set thread fields. */
|
||||
for (size_t i = 0; i < cpu::NumCores; i++) {
|
||||
m_running_threads[i] = nullptr;
|
||||
m_pinned_threads[i] = nullptr;
|
||||
m_running_thread_idle_counts[i] = 0;
|
||||
m_running_threads[i] = nullptr;
|
||||
m_pinned_threads[i] = nullptr;
|
||||
m_running_thread_idle_counts[i] = 0;
|
||||
m_running_thread_switch_counts[i] = 0;
|
||||
}
|
||||
|
||||
/* Set max memory based on address space type. */
|
||||
@@ -816,8 +820,8 @@ namespace ams::kern {
|
||||
m_exception_thread = nullptr;
|
||||
|
||||
/* Remove waiter thread. */
|
||||
s32 num_waiters;
|
||||
if (KThread *next = thread->RemoveWaiterByKey(std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread)) | 1); next != nullptr) {
|
||||
bool has_waiters;
|
||||
if (KThread *next = thread->RemoveWaiterByKey(std::addressof(has_waiters), reinterpret_cast<uintptr_t>(std::addressof(m_exception_thread)) | 1); next != nullptr) {
|
||||
next->EndWait(ResultSuccess());
|
||||
}
|
||||
|
||||
@@ -1241,7 +1245,8 @@ namespace ams::kern {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
if (m_is_jit_debug) {
|
||||
return KDebugBase::CreateDebugEvent(m_jit_debug_event_type, m_jit_debug_exception_type, m_jit_debug_params[0], m_jit_debug_params[1], m_jit_debug_params[2], m_jit_debug_params[3], m_jit_debug_thread_id);
|
||||
const uintptr_t params[5] = { m_jit_debug_exception_type, m_jit_debug_params[0], m_jit_debug_params[1], m_jit_debug_params[2], m_jit_debug_params[3] };
|
||||
return KDebugBase::CreateDebugEvent(m_jit_debug_event_type, m_jit_debug_thread_id, params, util::size(params));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -88,10 +88,12 @@ namespace ams::kern {
|
||||
if (m_state.should_count_idle) {
|
||||
if (AMS_LIKELY(highest_thread != nullptr)) {
|
||||
if (KProcess *process = highest_thread->GetOwnerProcess(); process != nullptr) {
|
||||
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
|
||||
/* Set running thread (and increment switch count). */
|
||||
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, ++m_state.switch_count);
|
||||
}
|
||||
} else {
|
||||
m_state.idle_count++;
|
||||
/* Set idle count and switch count to switch count + 1. */
|
||||
m_state.idle_count = ++m_state.switch_count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +243,7 @@ namespace ams::kern {
|
||||
if (AMS_LIKELY(!cur_thread->IsTerminationRequested()) && AMS_LIKELY(cur_thread->GetActiveCore() == m_core_id)) {
|
||||
m_state.prev_thread = cur_thread;
|
||||
} else {
|
||||
m_state.prev_thread =nullptr;
|
||||
m_state.prev_thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
namespace init {
|
||||
|
||||
/* TODO: Is this function name architecture specific? */
|
||||
void StartOtherCore(const ams::kern::init::KInitArguments *init_args);
|
||||
|
||||
}
|
||||
|
||||
/* Initialization. */
|
||||
size_t KSystemControlBase::Init::GetRealMemorySize() {
|
||||
return ams::kern::MainMemorySize;
|
||||
@@ -68,7 +75,7 @@ namespace ams::kern {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KSystemControlBase::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
void KSystemControlBase::Init::CpuOnImpl(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<0, false>(core_id, entrypoint, arg)) == 0);
|
||||
#else
|
||||
@@ -76,6 +83,22 @@ namespace ams::kern {
|
||||
#endif
|
||||
}
|
||||
|
||||
void KSystemControlBase::Init::TurnOnCpu(u64 core_id, const ams::kern::init::KInitArguments *args) {
|
||||
/* Get entrypoint. */
|
||||
KPhysicalAddress entrypoint = Null<KPhysicalAddress>;
|
||||
while (!cpu::GetPhysicalAddressReadable(std::addressof(entrypoint), reinterpret_cast<uintptr_t>(::ams::kern::init::StartOtherCore), true)) { /* ... */ }
|
||||
|
||||
/* Get arguments. */
|
||||
KPhysicalAddress args_addr = Null<KPhysicalAddress>;
|
||||
while (!cpu::GetPhysicalAddressReadable(std::addressof(args_addr), reinterpret_cast<uintptr_t>(args), true)) { /* ... */ }
|
||||
|
||||
/* Ensure cache is correct for the initial arguments. */
|
||||
cpu::StoreDataCacheForInitArguments(args, sizeof(*args));
|
||||
|
||||
/* Turn on the cpu. */
|
||||
KSystemControl::Init::CpuOnImpl(core_id, GetInteger(entrypoint), GetInteger(args_addr));
|
||||
}
|
||||
|
||||
/* Randomness for Initialization. */
|
||||
void KSystemControlBase::Init::GenerateRandom(u64 *dst, size_t count) {
|
||||
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||
@@ -100,23 +123,15 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* System Initialization. */
|
||||
void KSystemControlBase::InitializePhase1(bool skip_target_system) {
|
||||
/* Initialize the rng, if we somehow haven't already. */
|
||||
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||
const u64 seed = KHardwareTimer::GetTick();
|
||||
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||
s_initialized_random_generator = true;
|
||||
}
|
||||
|
||||
/* Configure KTargetSystem, if we haven't already by an implementation SystemControl. */
|
||||
if (!skip_target_system) {
|
||||
void KSystemControlBase::InitializePhase1() {
|
||||
/* Configure KTargetSystem. */
|
||||
{
|
||||
/* Set IsDebugMode. */
|
||||
{
|
||||
KTargetSystem::SetIsDebugMode(true);
|
||||
|
||||
/* If debug mode, we want to initialize uart logging. */
|
||||
KTargetSystem::EnableDebugLogging(true);
|
||||
KDebugLog::Initialize();
|
||||
}
|
||||
|
||||
/* Set Kernel Configuration. */
|
||||
@@ -135,6 +150,20 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize random and resource limit. */
|
||||
KSystemControlBase::InitializePhase1Base(KHardwareTimer::GetTick());
|
||||
}
|
||||
|
||||
void KSystemControlBase::InitializePhase1Base(u64 seed) {
|
||||
/* Initialize the rng, if we somehow haven't already. */
|
||||
if (AMS_UNLIKELY(!s_initialized_random_generator)) {
|
||||
s_random_generator.Initialize(reinterpret_cast<const u32*>(std::addressof(seed)), sizeof(seed) / sizeof(u32));
|
||||
s_initialized_random_generator = true;
|
||||
}
|
||||
|
||||
/* Initialize debug logging. */
|
||||
KDebugLog::Initialize();
|
||||
|
||||
/* System ResourceLimit initialization. */
|
||||
{
|
||||
/* Construct the resource limit object. */
|
||||
@@ -144,6 +173,19 @@ namespace ams::kern {
|
||||
|
||||
/* Set the initial limits. */
|
||||
const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
|
||||
|
||||
/* Update 39-bit address space infos. */
|
||||
{
|
||||
/* Heap should be equal to the total memory size, minimum 8 GB, maximum 32 GB. */
|
||||
/* Alias should be equal to 8 * heap size, maximum 128 GB. */
|
||||
const size_t heap_size = std::max(std::min(util::AlignUp(total_memory_size, 1_GB), 32_GB), 8_GB);
|
||||
const size_t alias_size = std::min(heap_size * 8, 128_GB);
|
||||
|
||||
/* Set the address space sizes. */
|
||||
KAddressSpaceInfo::SetAddressSpaceSize(39, KAddressSpaceInfo::Type_Heap, heap_size);
|
||||
KAddressSpaceInfo::SetAddressSpaceSize(39, KAddressSpaceInfo::Type_Alias, alias_size);
|
||||
}
|
||||
|
||||
const auto &slab_counts = init::GetSlabResourceCounts();
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace ams::kern {
|
||||
m_light_ipc_data = nullptr;
|
||||
|
||||
/* We're not waiting for a lock, and we haven't disabled migration. */
|
||||
m_lock_owner = nullptr;
|
||||
m_waiting_lock_info = nullptr;
|
||||
m_num_core_migration_disables = 0;
|
||||
|
||||
/* We have no waiters, and no closed objects. */
|
||||
@@ -397,25 +397,39 @@ namespace ams::kern {
|
||||
|
||||
/* Release any waiters. */
|
||||
{
|
||||
MESOSPHERE_ASSERT(m_lock_owner == nullptr);
|
||||
MESOSPHERE_ASSERT(m_waiting_lock_info == nullptr);
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
auto it = m_waiter_list.begin();
|
||||
while (it != m_waiter_list.end()) {
|
||||
/* Get the thread. */
|
||||
KThread * const waiter = std::addressof(*it);
|
||||
/* Check that we have no kernel waiters. */
|
||||
MESOSPHERE_ABORT_UNLESS(m_num_kernel_waiters == 0);
|
||||
|
||||
/* The thread shouldn't be a kernel waiter. */
|
||||
MESOSPHERE_ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
|
||||
auto it = m_held_lock_info_list.begin();
|
||||
while (it != m_held_lock_info_list.end()) {
|
||||
/* Get the lock info. */
|
||||
auto * const lock_info = std::addressof(*it);
|
||||
|
||||
/* Clear the lock owner. */
|
||||
waiter->SetLockOwner(nullptr);
|
||||
/* The lock shouldn't have a kernel waiter. */
|
||||
MESOSPHERE_ASSERT(!IsKernelAddressKey(lock_info->GetAddressKey()));
|
||||
|
||||
/* Erase the waiter from our list. */
|
||||
it = m_waiter_list.erase(it);
|
||||
/* Remove all waiters. */
|
||||
while (lock_info->GetWaiterCount() != 0) {
|
||||
/* Get the front waiter. */
|
||||
KThread * const waiter = lock_info->GetHighestPriorityWaiter();
|
||||
|
||||
/* Cancel the thread's wait. */
|
||||
waiter->CancelWait(svc::ResultInvalidState(), true);
|
||||
/* Remove it from the lock. */
|
||||
if (lock_info->RemoveWaiter(waiter)) {
|
||||
MESOSPHERE_ASSERT(lock_info->GetWaiterCount() == 0);
|
||||
}
|
||||
|
||||
/* Cancel the thread's wait. */
|
||||
waiter->CancelWait(svc::ResultInvalidState(), true);
|
||||
}
|
||||
|
||||
/* Remove the held lock from our list. */
|
||||
it = m_held_lock_info_list.erase(it);
|
||||
|
||||
/* Free the lock info. */
|
||||
LockWithPriorityInheritanceInfo::Free(lock_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,11 +837,8 @@ namespace ams::kern {
|
||||
void KThread::IncreaseBasePriority(s32 priority) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority);
|
||||
|
||||
/* Set our unpin base priority, if we're pinned. */
|
||||
if (this->GetStackParameters().is_pinned && m_base_priority_on_unpin > priority) {
|
||||
m_base_priority_on_unpin = priority;
|
||||
}
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
MESOSPHERE_ASSERT(!this->GetStackParameters().is_pinned);
|
||||
|
||||
/* Set our base priority. */
|
||||
if (m_base_priority > priority) {
|
||||
@@ -1044,28 +1055,58 @@ namespace ams::kern {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KThread::AddWaiterImpl(KThread *thread) {
|
||||
void KThread::AddHeldLock(LockWithPriorityInheritanceInfo *lock_info) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
/* Find the right spot to insert the waiter. */
|
||||
auto it = m_waiter_list.begin();
|
||||
while (it != m_waiter_list.end()) {
|
||||
if (it->GetPriority() > thread->GetPriority()) {
|
||||
break;
|
||||
/* Set ourselves as the lock's owner. */
|
||||
lock_info->SetOwner(this);
|
||||
|
||||
/* Add the lock to our held list. */
|
||||
m_held_lock_info_list.push_front(*lock_info);
|
||||
}
|
||||
|
||||
KThread::LockWithPriorityInheritanceInfo *KThread::FindHeldLock(KProcessAddress address_key) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
/* Try to find an existing held lock. */
|
||||
for (auto &held_lock : m_held_lock_info_list) {
|
||||
if (held_lock.GetAddressKey() == address_key) {
|
||||
return std::addressof(held_lock);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void KThread::AddWaiterImpl(KThread *thread) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
MESOSPHERE_ASSERT(thread->GetConditionVariableTree() == nullptr);
|
||||
|
||||
/* Get the thread's address key. */
|
||||
const auto address_key = thread->GetAddressKey();
|
||||
|
||||
/* Keep track of how many kernel waiters we have. */
|
||||
if (IsKernelAddressKey(thread->GetAddressKey())) {
|
||||
if (IsKernelAddressKey(address_key)) {
|
||||
MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters++) >= 0);
|
||||
KScheduler::SetSchedulerUpdateNeeded();
|
||||
}
|
||||
|
||||
/* Insert the waiter. */
|
||||
m_waiter_list.insert(it, *thread);
|
||||
thread->SetLockOwner(this);
|
||||
/* Get the relevant lock info. */
|
||||
auto *lock_info = this->FindHeldLock(address_key);
|
||||
if (lock_info == nullptr) {
|
||||
/* Create a new lock for the address key. */
|
||||
lock_info = LockWithPriorityInheritanceInfo::Create(address_key);
|
||||
|
||||
/* Add the new lock to our list. */
|
||||
this->AddHeldLock(lock_info);
|
||||
}
|
||||
|
||||
/* Add the thread as waiter to the lock info. */
|
||||
lock_info->AddWaiter(thread);
|
||||
}
|
||||
|
||||
void KThread::RemoveWaiterImpl(KThread *thread) {
|
||||
@@ -1078,19 +1119,25 @@ namespace ams::kern {
|
||||
KScheduler::SetSchedulerUpdateNeeded();
|
||||
}
|
||||
|
||||
/* Get the info for the lock the thread is waiting on. */
|
||||
auto *lock_info = thread->GetWaitingLockInfo();
|
||||
MESOSPHERE_ASSERT(lock_info->GetOwner() == this);
|
||||
|
||||
/* Remove the waiter. */
|
||||
m_waiter_list.erase(m_waiter_list.iterator_to(*thread));
|
||||
thread->SetLockOwner(nullptr);
|
||||
if (lock_info->RemoveWaiter(thread)) {
|
||||
m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info));
|
||||
LockWithPriorityInheritanceInfo::Free(lock_info);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::RestorePriority(KThread *thread) {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
while (true) {
|
||||
while (thread != nullptr) {
|
||||
/* We want to inherit priority where possible. */
|
||||
s32 new_priority = thread->GetBasePriority();
|
||||
if (thread->HasWaiters()) {
|
||||
new_priority = std::min(new_priority, thread->m_waiter_list.front().GetPriority());
|
||||
for (const auto &held_lock : thread->m_held_lock_info_list) {
|
||||
new_priority = std::min(new_priority, held_lock.GetHighestPriorityWaiter()->GetPriority());
|
||||
}
|
||||
|
||||
/* If the priority we would inherit is not different from ours, don't do anything. */
|
||||
@@ -1098,6 +1145,14 @@ namespace ams::kern {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the owner of whatever lock this thread is waiting on. */
|
||||
KThread * const lock_owner = thread->GetLockOwner();
|
||||
|
||||
/* If the thread is waiting on some lock, remove it as a waiter to prevent violating red black tree invariants. */
|
||||
if (lock_owner != nullptr) {
|
||||
lock_owner->RemoveWaiterImpl(thread);
|
||||
}
|
||||
|
||||
/* Ensure we don't violate condition variable red black tree invariants. */
|
||||
if (auto *cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
|
||||
BeforeUpdatePriority(cv_tree, thread);
|
||||
@@ -1112,73 +1167,94 @@ namespace ams::kern {
|
||||
AfterUpdatePriority(cv_tree, thread);
|
||||
}
|
||||
|
||||
/* If we removed the thread from some lock's waiting list, add it back. */
|
||||
if (lock_owner != nullptr) {
|
||||
lock_owner->AddWaiterImpl(thread);
|
||||
}
|
||||
|
||||
/* Update the scheduler. */
|
||||
KScheduler::OnThreadPriorityChanged(thread, old_priority);
|
||||
|
||||
/* Keep the lock owner up to date. */
|
||||
KThread *lock_owner = thread->GetLockOwner();
|
||||
if (lock_owner == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the thread in the lock owner's sorted list, and continue inheriting. */
|
||||
lock_owner->RemoveWaiterImpl(thread);
|
||||
lock_owner->AddWaiterImpl(thread);
|
||||
/* Continue inheriting priority. */
|
||||
thread = lock_owner;
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::AddWaiter(KThread *thread) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
this->AddWaiterImpl(thread);
|
||||
RestorePriority(this);
|
||||
|
||||
/* If the thread has a higher priority than us, we should inherit. */
|
||||
if (thread->GetPriority() < this->GetPriority()) {
|
||||
RestorePriority(this);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::RemoveWaiter(KThread *thread) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
this->RemoveWaiterImpl(thread);
|
||||
RestorePriority(this);
|
||||
|
||||
/* If our priority is the same as the thread's (and we've inherited), we may need to restore to lower priority. */
|
||||
if (this->GetPriority() == thread->GetPriority() && this->GetPriority() < this->GetBasePriority()) {
|
||||
RestorePriority(this);
|
||||
}
|
||||
}
|
||||
|
||||
KThread *KThread::RemoveWaiterByKey(s32 *out_num_waiters, KProcessAddress key) {
|
||||
KThread *KThread::RemoveWaiterByKey(bool *out_has_waiters, KProcessAddress key) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
s32 num_waiters = 0;
|
||||
KThread *next_lock_owner = nullptr;
|
||||
auto it = m_waiter_list.begin();
|
||||
while (it != m_waiter_list.end()) {
|
||||
if (it->GetAddressKey() == key) {
|
||||
KThread *thread = std::addressof(*it);
|
||||
/* Get the relevant lock info. */
|
||||
auto *lock_info = this->FindHeldLock(key);
|
||||
if (lock_info == nullptr) {
|
||||
*out_has_waiters = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Keep track of how many kernel waiters we have. */
|
||||
if (IsKernelAddressKey(thread->GetAddressKey())) {
|
||||
MESOSPHERE_ABORT_UNLESS((m_num_kernel_waiters--) > 0);
|
||||
KScheduler::SetSchedulerUpdateNeeded();
|
||||
}
|
||||
it = m_waiter_list.erase(it);
|
||||
/* Remove the lock info from our held list. */
|
||||
m_held_lock_info_list.erase(m_held_lock_info_list.iterator_to(*lock_info));
|
||||
|
||||
/* Update the next lock owner. */
|
||||
if (next_lock_owner == nullptr) {
|
||||
next_lock_owner = thread;
|
||||
next_lock_owner->SetLockOwner(nullptr);
|
||||
} else {
|
||||
next_lock_owner->AddWaiterImpl(thread);
|
||||
}
|
||||
num_waiters++;
|
||||
} else {
|
||||
it++;
|
||||
/* Keep track of how many kernel waiters we have. */
|
||||
if (IsKernelAddressKey(lock_info->GetAddressKey())) {
|
||||
m_num_kernel_waiters -= lock_info->GetWaiterCount();
|
||||
MESOSPHERE_ABORT_UNLESS(m_num_kernel_waiters >= 0);
|
||||
KScheduler::SetSchedulerUpdateNeeded();
|
||||
}
|
||||
|
||||
MESOSPHERE_ASSERT(lock_info->GetWaiterCount() > 0);
|
||||
|
||||
/* Remove the highest priority waiter from the lock to be the next owner. */
|
||||
KThread *next_lock_owner = lock_info->GetHighestPriorityWaiter();
|
||||
if (lock_info->RemoveWaiter(next_lock_owner)) {
|
||||
/* The new owner was the only waiter. */
|
||||
*out_has_waiters = false;
|
||||
|
||||
/* Free the lock info, since it has no waiters. */
|
||||
LockWithPriorityInheritanceInfo::Free(lock_info);
|
||||
} else {
|
||||
/* There are additional waiters on the lock. */
|
||||
*out_has_waiters = true;
|
||||
|
||||
/* Add the lock to the new owner's held list. */
|
||||
next_lock_owner->AddHeldLock(lock_info);
|
||||
|
||||
/* Keep track of any kernel waiters for the new owner. */
|
||||
if (IsKernelAddressKey(lock_info->GetAddressKey())) {
|
||||
next_lock_owner->m_num_kernel_waiters += lock_info->GetWaiterCount();
|
||||
MESOSPHERE_ABORT_UNLESS(next_lock_owner->m_num_kernel_waiters > 0);
|
||||
|
||||
/* NOTE: No need to set scheduler update needed, because we will have already done so when removing earlier. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Do priority updates, if we have a next owner. */
|
||||
if (next_lock_owner) {
|
||||
/* If our priority is the same as the next owner's (and we've inherited), we may need to restore to lower priority. */
|
||||
if (this->GetPriority() == next_lock_owner->GetPriority() && this->GetPriority() < this->GetBasePriority()) {
|
||||
RestorePriority(this);
|
||||
RestorePriority(next_lock_owner);
|
||||
/* NOTE: No need to restore priority on the next lock owner, because it was already the highest priority waiter on the lock. */
|
||||
}
|
||||
|
||||
/* Return output. */
|
||||
*out_num_waiters = num_waiters;
|
||||
/* Return the next lock owner. */
|
||||
return next_lock_owner;
|
||||
}
|
||||
|
||||
@@ -1309,9 +1385,7 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
/* Change the thread's priority to be higher than any system thread's. */
|
||||
if (this->GetBasePriority() >= ams::svc::SystemThreadPriorityHighest) {
|
||||
this->SetBasePriority(TerminatingThreadPriority);
|
||||
}
|
||||
this->IncreaseBasePriority(TerminatingThreadPriority);
|
||||
|
||||
/* If the thread is runnable, send a termination interrupt to other cores. */
|
||||
if (this->GetState() == ThreadState_Runnable) {
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace ams::kern::svc {
|
||||
/* Check whether the address is aligned. */
|
||||
const bool aligned = util::IsAligned(phys_addr, PageSize);
|
||||
|
||||
auto QueryIoMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
auto QueryMappingFromPageTable = [&](uint64_t phys_addr, size_t size) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
/* The size must be non-zero. */
|
||||
R_UNLESS(size > 0, svc::ResultInvalidSize());
|
||||
|
||||
@@ -56,7 +56,12 @@ namespace ams::kern::svc {
|
||||
R_UNLESS((phys_addr < phys_addr + size), svc::ResultNotFound());
|
||||
|
||||
/* Query the mapping. */
|
||||
R_TRY(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size));
|
||||
R_TRY_CATCH(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size)) {
|
||||
R_CATCH(svc::ResultNotFound) {
|
||||
/* If we failed to find an io mapping, check if the address is a static mapping. */
|
||||
R_TRY(pt.QueryStaticMapping(std::addressof(found_address), phys_addr, size));
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Use the size as the found size. */
|
||||
found_size = size;
|
||||
@@ -66,12 +71,12 @@ namespace ams::kern::svc {
|
||||
|
||||
if (aligned) {
|
||||
/* Query the input. */
|
||||
R_TRY(QueryIoMappingFromPageTable(phys_addr, size));
|
||||
R_TRY(QueryMappingFromPageTable(phys_addr, size));
|
||||
} else {
|
||||
if (kern::GetTargetFirmware() < TargetFirmware_8_0_0 && phys_addr >= PageSize) {
|
||||
/* Query the aligned-down page. */
|
||||
const size_t offset = phys_addr & (PageSize - 1);
|
||||
R_TRY(QueryIoMappingFromPageTable(phys_addr - offset, size + offset));
|
||||
R_TRY(QueryMappingFromPageTable(phys_addr - offset, size + offset));
|
||||
|
||||
/* Adjust the output address. */
|
||||
found_address += offset;
|
||||
|
||||
@@ -297,6 +297,19 @@ namespace ams::kern::svc {
|
||||
*out = GetCurrentProcess().IsPermittedSvc(static_cast<svc::SvcId>(info_subtype));
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_IoRegionHint:
|
||||
{
|
||||
/* Verify the sub-type is valid. */
|
||||
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||
|
||||
/* Get the io region from its handle. */
|
||||
KScopedAutoObject io_region = GetCurrentProcess().GetHandleTable().GetObject<KIoRegion>(handle);
|
||||
R_UNLESS(io_region.IsNotNull(), svc::ResultInvalidHandle());
|
||||
|
||||
/* Get the io region's address hint. */
|
||||
*out = io_region->GetHint();
|
||||
}
|
||||
break;
|
||||
case ams::svc::InfoType_MesosphereMeta:
|
||||
{
|
||||
/* Verify the handle is invalid. */
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace ams::kern::svc {
|
||||
|
||||
/* Get the process id. */
|
||||
*out_process_id = d->GetProcessUnsafe()->GetProcessId();
|
||||
} else {
|
||||
R_THROW(svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -174,6 +174,10 @@
|
||||
HANDLER(ServiceProfileInfo, 133) \
|
||||
HANDLER(BluetoothAudioInfo, 134) \
|
||||
HANDLER(BluetoothPairingCountInfo, 135) \
|
||||
HANDLER(FsProxyErrorInfo2, 136) \
|
||||
HANDLER(BuiltInWirelessOUIInfo, 137) \
|
||||
HANDLER(WirelessAPOUIInfo, 138) \
|
||||
HANDLER(EthernetAdapterOUIInfo, 139) \
|
||||
|
||||
#define AMS_ERPT_FOREACH_FIELD(HANDLER) \
|
||||
HANDLER(TestU64, 0, Test, FieldType_NumericU64, FieldFlag_None ) \
|
||||
@@ -840,4 +844,10 @@
|
||||
HANDLER(FatFsBisUserUniqueDirectoryEntryPeakOpenCount, 661, FsProxyErrorInfo, FieldType_NumericU16, FieldFlag_None ) \
|
||||
HANDLER(FatFsSdCardUniqueFileEntryPeakOpenCount, 662, FsProxyErrorInfo, FieldType_NumericU16, FieldFlag_None ) \
|
||||
HANDLER(FatFsSdCardUniqueDirectoryEntryPeakOpenCount, 663, FsProxyErrorInfo, FieldType_NumericU16, FieldFlag_None ) \
|
||||
HANDLER(ServerErrorIsRetryable, 664, ErrorInfo, FieldType_Bool, FieldFlag_None ) \
|
||||
HANDLER(FsDeepRetryStartCount, 665, FsProxyErrorInfo2, FieldType_NumericU32, FieldFlag_None ) \
|
||||
HANDLER(FsUnrecoverableByGameCardAccessFailedCount, 666, FsProxyErrorInfo2, FieldType_NumericU32, FieldFlag_None ) \
|
||||
HANDLER(BuiltInWirelessOUI, 667, BuiltInWirelessOUIInfo, FieldType_String, FieldFlag_None ) \
|
||||
HANDLER(WirelessAPOUI, 668, WirelessAPOUIInfo, FieldType_String, FieldFlag_None ) \
|
||||
HANDLER(EthernetAdapterOUI, 669, EthernetAdapterOUIInfo, FieldType_String, FieldFlag_None ) \
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace ams::erpt::srv {
|
||||
|
||||
constexpr inline const char ReportOnSdStoragePath[] = "ersd";
|
||||
constexpr inline const char ReportOnSdStorageRootDirectoryPath[] = "ersd:/";
|
||||
|
||||
constexpr inline const char ReportStoragePath[] = "save";
|
||||
constexpr inline const char JournalFileName[] = "save:/journal";
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace ams::fs {
|
||||
SystemProperEncryption = 32,
|
||||
SystemProperPartition = 33,
|
||||
SignedSystemPartitionOnSafeMode = 34,
|
||||
DeviceTreeBlob = 35,
|
||||
System0 = 36,
|
||||
};
|
||||
|
||||
const char *GetBisMountName(BisPartitionId id);
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
Result MountCode(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id);
|
||||
/* ACCURATE_TO_VERSION: 16.2.0.0 */
|
||||
Result MountCode(CodeVerificationData *out, const char *name, const char *path, fs::ContentAttributes attr, ncm::ProgramId program_id);
|
||||
|
||||
Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific);
|
||||
Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id);
|
||||
Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, fs::ContentAttributes attr, ncm::ProgramId program_id, bool is_hbl, bool is_specific);
|
||||
Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, fs::ContentAttributes attr, ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace ams::fs {
|
||||
ContentType_Data = 4,
|
||||
};
|
||||
|
||||
Result MountContent(const char *name, const char *path, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, fs::ContentAttributes attr, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, fs::ContentAttributes attr, ncm::ProgramId id, ContentType content_type);
|
||||
Result MountContent(const char *name, const char *path, fs::ContentAttributes attr, ncm::DataId id, ContentType content_type);
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace ams::fs {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
enum ContentAttributes : u8 {
|
||||
ContentAttributes_None = 0,
|
||||
ContentAttributes_None = 0x0,
|
||||
ContentAttributes_All = 0xF,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -15,16 +15,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere/fs/fs_common.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage_id.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
enum class ContentStorageId : u32 {
|
||||
System = 0,
|
||||
User = 1,
|
||||
SdCard = 2,
|
||||
};
|
||||
|
||||
constexpr inline const char * const ContentStorageDirectoryName = "Contents";
|
||||
|
||||
const char *GetContentStorageMountName(ContentStorageId id);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 16.2.0.0 */
|
||||
enum class ContentStorageId : u32 {
|
||||
System = 0,
|
||||
User = 1,
|
||||
SdCard = 2,
|
||||
System0 = 3,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/fs_content_attributes.hpp>
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace ams::fs {
|
||||
constexpr inline RightsId InvalidRightsId = {};
|
||||
|
||||
/* Rights ID API */
|
||||
Result GetRightsId(RightsId *out, const char *path);
|
||||
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path);
|
||||
Result GetRightsId(RightsId *out, const char *path, fs::ContentAttributes attr);
|
||||
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path, fs::ContentAttributes attr);
|
||||
|
||||
}
|
||||
@@ -50,8 +50,9 @@ namespace ams::fssrv {
|
||||
Result SetCurrentProcess(const ams::sf::ClientProcessId &client_pid);
|
||||
Result OpenDataFileSystemByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out);
|
||||
Result OpenFileSystemWithPatch(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type);
|
||||
Result OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type);
|
||||
Result OpenFileSystemWithIdObsolete(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type);
|
||||
Result OpenDataFileSystemByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id);
|
||||
Result OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, fs::ContentAttributes attr, u64 program_id, u32 type);
|
||||
Result OpenBisFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id);
|
||||
Result OpenBisStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id);
|
||||
Result InvalidateBisCache();
|
||||
@@ -90,7 +91,8 @@ namespace ams::fssrv {
|
||||
Result OpenPatchDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out);
|
||||
Result OpenDataFileSystemWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index);
|
||||
Result OpenDataStorageWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index);
|
||||
Result OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type);
|
||||
Result OpenDataStorageByPathObsolete(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type);
|
||||
Result OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, fs::ContentAttributes attr, u32 type);
|
||||
Result OpenDeviceOperator(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out);
|
||||
Result OpenSdCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out);
|
||||
Result OpenGameCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out);
|
||||
@@ -103,7 +105,8 @@ namespace ams::fssrv {
|
||||
Result RegisterExternalKey(const fs::RightsId &rights_id, const spl::AccessKey &access_key);
|
||||
Result UnregisterAllExternalKey();
|
||||
Result GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path);
|
||||
Result GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path);
|
||||
Result GetRightsIdAndKeyGenerationByPathObsolete(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path);
|
||||
Result GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path, fs::ContentAttributes attr);
|
||||
Result SetCurrentPosixTimeWithTimeDifference(s64 posix_time, s32 time_difference);
|
||||
Result GetFreeSpaceSizeForSaveData(ams::sf::Out<s64> out, u8 space_id);
|
||||
Result VerifySaveDataFileSystemBySaveDataSpaceId();
|
||||
@@ -142,7 +145,8 @@ namespace ams::fssrv {
|
||||
public:
|
||||
/* fsp-ldr */
|
||||
Result OpenCodeFileSystemDeprecated(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id);
|
||||
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id);
|
||||
Result OpenCodeFileSystemDeprecated2(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id);
|
||||
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, fs::ContentAttributes attr, ncm::ProgramId program_id);
|
||||
Result IsArchivedProgram(ams::sf::Out<bool> out, u64 process_id);
|
||||
};
|
||||
static_assert(sf::IsIFileSystemProxy<FileSystemProxyImpl>);
|
||||
@@ -156,11 +160,16 @@ namespace ams::fssrv {
|
||||
R_THROW(fs::ResultPortAcceptableCountLimited());
|
||||
}
|
||||
|
||||
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
|
||||
Result OpenCodeFileSystemDeprecated2(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
|
||||
AMS_UNUSED(out_fs, out_verif, path, program_id);
|
||||
R_THROW(fs::ResultPortAcceptableCountLimited());
|
||||
}
|
||||
|
||||
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, fs::ContentAttributes attr, ncm::ProgramId program_id) {
|
||||
AMS_UNUSED(out_fs, out_verif, path, attr, program_id);
|
||||
R_THROW(fs::ResultPortAcceptableCountLimited());
|
||||
}
|
||||
|
||||
Result IsArchivedProgram(ams::sf::Out<bool> out, u64 process_id) {
|
||||
AMS_UNUSED(out, process_id);
|
||||
R_THROW(fs::ResultPortAcceptableCountLimited());
|
||||
|
||||
@@ -27,8 +27,9 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, SetCurrentProcess, (const ams::sf::ClientProcessId &client_pid), (client_pid)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, OpenDataFileSystemByCurrentProcess, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, OpenFileSystemWithPatch, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type), (out, program_id, type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithId, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type), (out, path, program_id, type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithIdObsolete, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type), (out, path, program_id, type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, OpenDataFileSystemByProgramId, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id), (out, program_id), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithId, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, fs::ContentAttributes attr, u64 program_id, u32 type), (out, path, attr, program_id, type), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, OpenBisFileSystem, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id), (out, path, id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, OpenBisStorage, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id), (out, id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, InvalidateBisCache, (), ()) \
|
||||
@@ -87,7 +88,8 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 203, Result, OpenPatchDataStorageByCurrentProcess, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 204, Result, OpenDataFileSystemWithProgramIndex, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index), (out, index), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 205, Result, OpenDataStorageWithProgramIndex, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index), (out, index), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 206, Result, OpenDataStorageByPath, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type), (out, path, type), hos::Version_13_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 206, Result, OpenDataStorageByPathObsolete, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type), (out, path, type), hos::Version_13_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 206, Result, OpenDataStorageByPath, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, fs::ContentAttributes attr, u32 type), (out, path, attr, type), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 400, Result, OpenDeviceOperator, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 500, Result, OpenSdCardDetectionEventNotifier, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 501, Result, OpenGameCardDetectionEventNotifier, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out), (out)) \
|
||||
@@ -103,8 +105,9 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 606, Result, GetRightsId, (ams::sf::Out<fs::RightsId> out, ncm::ProgramId program_id, ncm::StorageId storage_id), (out, program_id, storage_id), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 607, Result, RegisterExternalKey, (const fs::RightsId &rights_id, const spl::AccessKey &access_key), (rights_id, access_key), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 608, Result, UnregisterAllExternalKey, (), (), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 609, Result, GetRightsIdByPath, (ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path), (out, path), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 610, Result, GetRightsIdAndKeyGenerationByPath, (ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path), (out, out_key_generation, path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 609, Result, GetRightsIdByPath, (ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path), (out, path), hos::Version_2_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 610, Result, GetRightsIdAndKeyGenerationByPathObsolete, (ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path), (out, out_key_generation, path), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 610, Result, GetRightsIdAndKeyGenerationByPath, (ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path, fs::ContentAttributes attr), (out, out_key_generation, path, attr), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 611, Result, SetCurrentPosixTimeWithTimeDifference, (s64 posix_time, s32 time_difference), (posix_time, time_difference), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 612, Result, GetFreeSpaceSizeForSaveData, (ams::sf::Out<s64> out, u8 space_id), (out, space_id), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 613, Result, VerifySaveDataFileSystemBySaveDataSpaceId, (), (), hos::Version_4_0_0) \
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
#include <stratosphere/sf.hpp>
|
||||
#include <stratosphere/fssrv/sf/fssrv_sf_ifilesystem.hpp>
|
||||
#include <stratosphere/fs/fs_code_verification_data.hpp>
|
||||
#include <stratosphere/fs/fs_content_attributes.hpp>
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
#define AMS_FSSRV_I_FILE_SYSTEM_PROXY_FOR_LOADER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenCodeFileSystemDeprecated, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id), (out_fs, path, program_id), hos::Version_Min, hos::Version_9_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenCodeFileSystem, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id), (out_fs, out_verif, path, program_id), hos::Version_10_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, IsArchivedProgram, (ams::sf::Out<bool> out, u64 process_id), (out, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetCurrentProcess, (const ams::sf::ClientProcessId &client_pid), (client_pid), hos::Version_4_0_0)
|
||||
#define AMS_FSSRV_I_FILE_SYSTEM_PROXY_FOR_LOADER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenCodeFileSystemDeprecated, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id), (out_fs, path, program_id), hos::Version_Min, hos::Version_9_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenCodeFileSystemDeprecated2, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id), (out_fs, out_verif, path, program_id), hos::Version_10_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenCodeFileSystem, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, fs::ContentAttributes attr, ncm::ProgramId program_id), (out_fs, out_verif, path, attr, program_id), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, IsArchivedProgram, (ams::sf::Out<bool> out, u64 process_id), (out, process_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetCurrentProcess, (const ams::sf::ClientProcessId &client_pid), (client_pid), hos::Version_4_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IFileSystemProxyForLoader, AMS_FSSRV_I_FILE_SYSTEM_PROXY_FOR_LOADER_INTERFACE_INFO, 0xDC92EE15)
|
||||
|
||||
@@ -74,6 +74,8 @@ namespace ams::hos {
|
||||
Version_14_1_1 = ::ams::TargetFirmware_14_1_1,
|
||||
Version_14_1_2 = ::ams::TargetFirmware_14_1_2,
|
||||
Version_15_0_0 = ::ams::TargetFirmware_15_0_0,
|
||||
Version_15_0_1 = ::ams::TargetFirmware_15_0_1,
|
||||
Version_16_0_0 = ::ams::TargetFirmware_16_0_0,
|
||||
|
||||
Version_Current = ::ams::TargetFirmware_Current,
|
||||
|
||||
|
||||
@@ -279,6 +279,23 @@ namespace ams::kvdb {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InitializeForReadOnlyArchiveFile(const char *path, size_t capacity, MemoryResource *mr) {
|
||||
/* Ensure that the passed path is a directory. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
R_TRY(fs::GetEntryType(std::addressof(entry_type), path));
|
||||
R_UNLESS(entry_type == fs::DirectoryEntryType_File, fs::ResultPathNotFound());
|
||||
|
||||
/* Set paths. */
|
||||
m_path.Assign(path);
|
||||
m_temp_path.Assign("");
|
||||
|
||||
/* Initialize our index. */
|
||||
R_TRY(m_index.Initialize(capacity, mr));
|
||||
m_memory_resource = mr;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Initialize(size_t capacity, MemoryResource *mr) {
|
||||
/* This initializes without an archive file. */
|
||||
/* A store initialized this way cannot have its contents loaded from or flushed to disk. */
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stratosphere/ncm/ncm_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_i_content_manager.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage_id.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
@@ -46,6 +47,8 @@ namespace ams::ncm {
|
||||
|
||||
Result InvalidateRightsIdCache();
|
||||
|
||||
Result ActivateFsContentStorage(fs::ContentStorageId fs_content_storage_id);
|
||||
|
||||
/* Deprecated API. */
|
||||
Result CloseContentStorageForcibly(StorageId storage_id);
|
||||
Result CloseContentMetaDatabaseForcibly(StorageId storage_id);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
struct alignas(4) ContentId {
|
||||
struct ContentId {
|
||||
util::Uuid uuid;
|
||||
|
||||
bool operator==(const ContentId &other) const {
|
||||
@@ -38,7 +38,7 @@ namespace ams::ncm {
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(alignof(ContentId) == 4);
|
||||
static_assert(alignof(ContentId) == 1);
|
||||
|
||||
constexpr inline ContentId InvalidContentId = { util::InvalidUuid };
|
||||
|
||||
|
||||
@@ -21,9 +21,10 @@ namespace ams::ncm {
|
||||
struct ContentManagerConfig {
|
||||
bool build_system_database;
|
||||
bool import_database_from_system_on_sd;
|
||||
bool enable_integrated_system_content;
|
||||
|
||||
bool HasAnyConfig() const {
|
||||
return this->ShouldBuildDatabase() || this->import_database_from_system_on_sd;
|
||||
return this->ShouldBuildDatabase() || this->import_database_from_system_on_sd || this->enable_integrated_system_content;
|
||||
}
|
||||
|
||||
bool ShouldBuildDatabase() const {
|
||||
@@ -33,6 +34,10 @@ namespace ams::ncm {
|
||||
bool ShouldImportDatabaseFromSignedSystemPartitionOnSd() const {
|
||||
return this->import_database_from_system_on_sd;
|
||||
}
|
||||
|
||||
bool IsIntegratedSystemContentEnabled() const {
|
||||
return this->enable_integrated_system_content;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <stratosphere/ncm/ncm_content_management_utils.hpp>
|
||||
#include <stratosphere/ncm/ncm_content_meta_utils.hpp>
|
||||
#include <stratosphere/ncm/ncm_registered_host_content.hpp>
|
||||
#include <stratosphere/ncm/ncm_integrated_content_meta_database_impl.hpp>
|
||||
#include <stratosphere/ncm/ncm_integrated_content_storage_impl.hpp>
|
||||
#include <stratosphere/kvdb/kvdb_memory_key_value_store.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
@@ -69,10 +71,29 @@ namespace ams::ncm {
|
||||
};
|
||||
static_assert(util::is_pod<SystemSaveDataInfo>::value);
|
||||
|
||||
struct IntegratedContentStorageImpl;
|
||||
|
||||
class ContentManagerImpl {
|
||||
private:
|
||||
constexpr static size_t MaxContentStorageRoots = 8;
|
||||
constexpr static size_t MaxContentMetaDatabaseRoots = 8;
|
||||
constexpr static size_t MaxContentStorageRoots = 8;
|
||||
constexpr static size_t MaxIntegratedContentStorageRoots = 8;
|
||||
constexpr static size_t MaxContentMetaDatabaseRoots = 8;
|
||||
constexpr static size_t MaxIntegratedContentMetaDatabaseRoots = 8;
|
||||
constexpr static size_t MaxConfigs = 8;
|
||||
constexpr static size_t MaxIntegratedConfigs = 8;
|
||||
private:
|
||||
struct ContentStorageConfig {
|
||||
fs::ContentStorageId content_storage_id;
|
||||
bool skip_verify_and_create;
|
||||
bool skip_activate;
|
||||
};
|
||||
|
||||
struct IntegratedContentStorageConfig {
|
||||
ncm::StorageId storage_id;
|
||||
fs::ContentStorageId content_storage_ids[MaxContentStorageRoots];
|
||||
int num_content_storage_ids;
|
||||
bool is_integrated;
|
||||
};
|
||||
private:
|
||||
struct ContentStorageRoot {
|
||||
NON_COPYABLE(ContentStorageRoot);
|
||||
@@ -81,10 +102,42 @@ namespace ams::ncm {
|
||||
char mount_name[fs::MountNameLengthMax + 1];
|
||||
char path[128];
|
||||
StorageId storage_id;
|
||||
fs::ContentStorageId content_storage_id;
|
||||
util::optional<ContentStorageConfig> config;
|
||||
sf::SharedPointer<IContentStorage> content_storage;
|
||||
|
||||
ContentStorageRoot() : mount_name(), path(), storage_id(), content_storage_id(), content_storage() { /* ... */ }
|
||||
ContentStorageRoot() : mount_name(), path(), storage_id(), config(util::nullopt), content_storage() { /* ... */ }
|
||||
};
|
||||
|
||||
struct IntegratedContentStorageRoot {
|
||||
NON_COPYABLE(IntegratedContentStorageRoot);
|
||||
NON_MOVEABLE(IntegratedContentStorageRoot);
|
||||
|
||||
const IntegratedContentStorageConfig *m_config;
|
||||
ContentStorageRoot *m_roots;
|
||||
int m_num_roots;
|
||||
sf::EmplacedRef<IContentStorage, IntegratedContentStorageImpl> m_integrated_content_storage;
|
||||
|
||||
IntegratedContentStorageRoot() : m_config(), m_roots(), m_num_roots(), m_integrated_content_storage() { /* ... */ }
|
||||
|
||||
Result Create();
|
||||
Result Verify();
|
||||
Result Open(sf::Out<sf::SharedPointer<IContentStorage>> out, RightsIdCache &rights_id_cache, RegisteredHostContent ®istered_host_content);
|
||||
Result Activate(RightsIdCache &rights_id_cache, RegisteredHostContent ®istered_host_content);
|
||||
Result Inactivate(RegisteredHostContent ®istered_host_content);
|
||||
|
||||
Result Activate(ContentStorageRoot &root, RightsIdCache &rights_id_cache, RegisteredHostContent ®istered_host_content);
|
||||
|
||||
Result Activate(RightsIdCache &rights_id_cache, RegisteredHostContent ®istered_host_content, fs::ContentStorageId content_storage_id);
|
||||
|
||||
ContentStorageRoot *GetRoot(fs::ContentStorageId storage_id) {
|
||||
for (auto i = 0; i < m_num_roots; ++i) {
|
||||
if (auto &root = m_roots[i]; root.config.has_value() && root.config->content_storage_id == storage_id) {
|
||||
return std::addressof(root);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct ContentMetaDatabaseRoot {
|
||||
@@ -94,46 +147,100 @@ namespace ams::ncm {
|
||||
char mount_name[fs::MountNameLengthMax + 1];
|
||||
char path[128];
|
||||
StorageId storage_id;
|
||||
SystemSaveDataInfo info;
|
||||
sf::SharedPointer<IContentMetaDatabase> content_meta_database;
|
||||
util::optional<ContentStorageConfig> storage_config;
|
||||
util::optional<SystemSaveDataInfo> save_data_info;
|
||||
util::optional<kvdb::MemoryKeyValueStore<ContentMetaKey>> kvs;
|
||||
sf::SharedPointer<IContentMetaDatabase> content_meta_database;
|
||||
ContentMetaMemoryResource *memory_resource;
|
||||
u32 max_content_metas;
|
||||
|
||||
ContentMetaDatabaseRoot() : mount_name(), path(), storage_id(), info(), content_meta_database(), kvs(util::nullopt), memory_resource(), max_content_metas() { /* ... */ }
|
||||
ContentMetaDatabaseRoot() : mount_name(), path(), storage_id(), storage_config(util::nullopt), save_data_info(util::nullopt), kvs(util::nullopt), content_meta_database(), memory_resource(), max_content_metas() { /* ... */ }
|
||||
};
|
||||
|
||||
struct IntegratedContentMetaDatabaseRoot {
|
||||
NON_COPYABLE(IntegratedContentMetaDatabaseRoot);
|
||||
NON_MOVEABLE(IntegratedContentMetaDatabaseRoot);
|
||||
|
||||
const IntegratedContentStorageConfig *m_config;
|
||||
ContentMetaDatabaseRoot *m_roots;
|
||||
int m_num_roots;
|
||||
sf::EmplacedRef<IContentMetaDatabase, IntegratedContentMetaDatabaseImpl> m_integrated_content_meta_database;
|
||||
|
||||
IntegratedContentMetaDatabaseRoot() : m_config(), m_roots(), m_num_roots(), m_integrated_content_meta_database() { /* ... */ }
|
||||
|
||||
Result Create();
|
||||
Result Verify();
|
||||
Result Open(sf::Out<sf::SharedPointer<IContentMetaDatabase>> out);
|
||||
Result Cleanup();
|
||||
Result Activate();
|
||||
Result Inactivate();
|
||||
|
||||
Result Activate(ContentMetaDatabaseRoot &root);
|
||||
|
||||
Result Activate(fs::ContentStorageId content_storage_id);
|
||||
|
||||
ContentMetaDatabaseRoot *GetRoot(fs::ContentStorageId storage_id) {
|
||||
for (auto i = 0; i < m_num_roots; ++i) {
|
||||
if (auto &root = m_roots[i]; root.storage_config.has_value() && root.storage_config->content_storage_id == storage_id) {
|
||||
return std::addressof(root);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
private:
|
||||
os::SdkRecursiveMutex m_mutex;
|
||||
bool m_initialized;
|
||||
ContentStorageRoot m_content_storage_roots[MaxContentStorageRoots];
|
||||
ContentMetaDatabaseRoot m_content_meta_database_roots[MaxContentMetaDatabaseRoots];
|
||||
u32 m_num_content_storage_entries;
|
||||
u32 m_num_content_meta_entries;
|
||||
RightsIdCache m_rights_id_cache;
|
||||
RegisteredHostContent m_registered_host_content;
|
||||
os::SdkRecursiveMutex m_mutex{};
|
||||
bool m_initialized{false};
|
||||
IntegratedContentStorageRoot m_integrated_content_storage_roots[MaxIntegratedContentStorageRoots]{};
|
||||
ContentStorageRoot m_content_storage_roots[MaxContentStorageRoots]{};
|
||||
IntegratedContentMetaDatabaseRoot m_integrated_content_meta_database_roots[MaxIntegratedContentMetaDatabaseRoots]{};
|
||||
ContentMetaDatabaseRoot m_content_meta_database_roots[MaxContentMetaDatabaseRoots]{};
|
||||
IntegratedContentStorageConfig m_integrated_configs[MaxIntegratedConfigs]{};
|
||||
ContentStorageConfig m_configs[MaxConfigs]{};
|
||||
u32 m_num_integrated_content_storage_entries{0};
|
||||
u32 m_num_content_storage_entries{0};
|
||||
u32 m_num_integrated_content_meta_entries{0};
|
||||
u32 m_num_content_meta_entries{0};
|
||||
u32 m_num_integrated_configs{0};
|
||||
u32 m_num_configs{0};
|
||||
RightsIdCache m_rights_id_cache{};
|
||||
RegisteredHostContent m_registered_host_content{};
|
||||
public:
|
||||
ContentManagerImpl() : m_mutex(), m_initialized(false), m_content_storage_roots(), m_content_meta_database_roots(), m_num_content_storage_entries(0), m_num_content_meta_entries(0), m_rights_id_cache(), m_registered_host_content() {
|
||||
/* ... */
|
||||
};
|
||||
ContentManagerImpl() = default;
|
||||
~ContentManagerImpl();
|
||||
public:
|
||||
Result Initialize(const ContentManagerConfig &config);
|
||||
private:
|
||||
Result Initialize(const ContentManagerConfig &manager_config, const IntegratedContentStorageConfig *integrated_configs, size_t num_integrated_configs, const ContentStorageConfig *configs, size_t num_configs, const ncm::StorageId *activated_storages, size_t num_activated_storages);
|
||||
Result InitializeStorageBuiltInSystem(const ContentManagerConfig &manager_config);
|
||||
Result InitializeStorage(ncm::StorageId storage_id);
|
||||
|
||||
const ContentStorageConfig &GetContentStorageConfig(fs::ContentStorageId content_storage_id) {
|
||||
for (size_t i = 0; i < m_num_configs; ++i) {
|
||||
if (m_configs[i].content_storage_id == content_storage_id) {
|
||||
return m_configs[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: Nintendo accesses out of bounds memory here. Should we explicitly abort? This is guaranteed by data to never happen. */
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
private:
|
||||
/* Helpers. */
|
||||
Result GetContentStorageRoot(ContentStorageRoot **out, StorageId id);
|
||||
Result GetContentMetaDatabaseRoot(ContentMetaDatabaseRoot **out, StorageId id);
|
||||
Result GetIntegratedContentStorageConfig(IntegratedContentStorageConfig **out, fs::ContentStorageId content_storage_id);
|
||||
Result GetIntegratedContentStorageRoot(IntegratedContentStorageRoot **out, StorageId id);
|
||||
Result GetIntegratedContentMetaDatabaseRoot(IntegratedContentMetaDatabaseRoot **out, StorageId id);
|
||||
|
||||
Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, fs::ContentStorageId content_storage_id);
|
||||
Result InitializeGameCardContentStorageRoot(ContentStorageRoot *out);
|
||||
Result InitializeContentStorageRoot(ContentStorageRoot *out, StorageId storage_id, util::optional<ContentStorageConfig> config);
|
||||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, util::optional<ContentStorageConfig> storage_config);
|
||||
|
||||
Result InitializeContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, StorageId storage_id, const SystemSaveDataInfo &info, size_t max_content_metas, ContentMetaMemoryResource *mr);
|
||||
Result InitializeGameCardContentMetaDatabaseRoot(ContentMetaDatabaseRoot *out, size_t max_content_metas, ContentMetaMemoryResource *mr);
|
||||
Result InitializeIntegratedContentStorageRoot(IntegratedContentStorageRoot *out, const IntegratedContentStorageConfig *config, size_t root_idx, size_t root_count);
|
||||
Result InitializeIntegratedContentMetaDatabaseRoot(IntegratedContentMetaDatabaseRoot *out, const IntegratedContentStorageConfig *config, size_t root_idx, size_t root_count);
|
||||
|
||||
Result BuildContentMetaDatabase(StorageId storage_id);
|
||||
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);
|
||||
Result ImportContentMetaDatabaseImpl(StorageId storage_id, const char *import_mount_name, const char *path);
|
||||
|
||||
Result EnsureAndMountSystemSaveData(const char *mount, const SystemSaveDataInfo &info) const;
|
||||
Result ImportContentMetaDatabaseImpl(ContentMetaDatabaseRoot *root, const char *import_mount_name);
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result CreateContentStorage(StorageId storage_id);
|
||||
@@ -151,6 +258,7 @@ namespace ams::ncm {
|
||||
Result InactivateContentMetaDatabase(StorageId storage_id);
|
||||
Result InvalidateRightsIdCache();
|
||||
Result GetMemoryReport(sf::Out<MemoryReport> out);
|
||||
Result ActivateFsContentStorage(fs::ContentStorageId fs_content_storage_id);
|
||||
};
|
||||
static_assert(IsIContentManager<ContentManagerImpl>);
|
||||
|
||||
|
||||
@@ -172,7 +172,14 @@ namespace ams::ncm {
|
||||
|
||||
Result GetPatchId(PatchId *out_patch_id, const ContentMetaKey &key) {
|
||||
AMS_ASSERT(m_interface != nullptr);
|
||||
R_RETURN(m_interface->GetPatchId(out_patch_id, key));
|
||||
static_assert(sizeof(*out_patch_id) == sizeof(u64));
|
||||
R_RETURN(m_interface->GetPatchContentMetaId(reinterpret_cast<u64 *>(out_patch_id), key));
|
||||
}
|
||||
|
||||
Result GetDataPatchId(DataPatchId *out_patch_id, const ContentMetaKey &key) {
|
||||
AMS_ASSERT(m_interface != nullptr);
|
||||
static_assert(sizeof(*out_patch_id) == sizeof(u64));
|
||||
R_RETURN(m_interface->GetPatchContentMetaId(reinterpret_cast<u64 *>(out_patch_id), key));
|
||||
}
|
||||
|
||||
Result DisableForcibly() {
|
||||
|
||||
@@ -23,17 +23,22 @@
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
using MountContentMetaFunction = Result (*)(const char *mount_name, const char *path);
|
||||
using MountContentMetaFunction = Result (*)(const char *mount_name, const char *path, fs::ContentAttributes attr);
|
||||
|
||||
bool IsContentMetaFileName(const char *name);
|
||||
|
||||
Result ReadContentMetaPathAlongWithExtendedDataAndDigest(AutoBuffer *out, const char *path);
|
||||
Result ReadContentMetaPathAlongWithExtendedDataAndDigestSuppressingFsAbort(AutoBuffer *out, const char *path);
|
||||
Result ReadContentMetaPathAlongWithExtendedDataAndDigest(AutoBuffer *out, const char *path, fs::ContentAttributes attr);
|
||||
Result ReadContentMetaPathAlongWithExtendedDataAndDigestSuppressingFsAbort(AutoBuffer *out, const char *path, fs::ContentAttributes attr);
|
||||
|
||||
Result ReadContentMetaPathWithoutExtendedDataOrDigest(AutoBuffer *out, const char *path);
|
||||
Result ReadContentMetaPathWithoutExtendedDataOrDigestSuppressingFsAbort(AutoBuffer *out, const char *path);
|
||||
Result ReadContentMetaPathWithoutExtendedDataOrDigest(AutoBuffer *out, const char *path, fs::ContentAttributes attr);
|
||||
Result ReadContentMetaPathWithoutExtendedDataOrDigestSuppressingFsAbort(AutoBuffer *out, const char *path, fs::ContentAttributes attr);
|
||||
|
||||
Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const Path &path, FirmwareVariationId firmware_variation_id);
|
||||
using ReadContentMetaPathFunction = Result (*)(AutoBuffer *out, const char *path, fs::ContentAttributes attr);
|
||||
|
||||
Result TryReadContentMetaPath(fs::ContentAttributes *out_attr, AutoBuffer *out, const char *path, ReadContentMetaPathFunction func);
|
||||
Result TryReadContentMetaPath(AutoBuffer *out, const char *path, ReadContentMetaPathFunction func);
|
||||
|
||||
Result ReadVariationContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const Path &path, fs::ContentAttributes attr, FirmwareVariationId firmware_variation_id);
|
||||
|
||||
void SetMountContentMetaFunction(MountContentMetaFunction func);
|
||||
|
||||
|
||||
@@ -142,12 +142,14 @@ namespace ams::ncm {
|
||||
R_RETURN(m_interface->ReadContentIdFile(sf::OutBuffer(dst, size), content_id, offset));
|
||||
}
|
||||
|
||||
Result GetRightsId(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id) {
|
||||
Result GetRightsId(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr) {
|
||||
AMS_ASSERT(m_interface != nullptr);
|
||||
|
||||
const auto vers = hos::GetVersion();
|
||||
if (vers >= hos::Version_3_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id));
|
||||
if (vers >= hos::Version_16_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderId(out_rights_id, placeholder_id, attr));
|
||||
} else if (vers >= hos::Version_3_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderIdDeprecated2(out_rights_id, placeholder_id));
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(vers >= hos::Version_2_0_0);
|
||||
*out_rights_id = {};
|
||||
@@ -155,12 +157,14 @@ namespace ams::ncm {
|
||||
}
|
||||
}
|
||||
|
||||
Result GetRightsId(ncm::RightsId *out_rights_id, ContentId content_id) {
|
||||
Result GetRightsId(ncm::RightsId *out_rights_id, ContentId content_id, fs::ContentAttributes attr) {
|
||||
AMS_ASSERT(m_interface != nullptr);
|
||||
|
||||
const auto vers = hos::GetVersion();
|
||||
if (vers >= hos::Version_3_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromContentId(out_rights_id, content_id));
|
||||
if (vers >= hos::Version_16_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromContentId(out_rights_id, content_id, attr));
|
||||
} else if (vers >= hos::Version_3_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromContentIdDeprecated2(out_rights_id, content_id));
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(vers >= hos::Version_2_0_0);
|
||||
*out_rights_id = {};
|
||||
@@ -193,9 +197,15 @@ namespace ams::ncm {
|
||||
R_RETURN(m_interface->RepairInvalidFileAttribute());
|
||||
}
|
||||
|
||||
Result GetRightsIdFromPlaceHolderIdWithCache(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id) {
|
||||
Result GetRightsIdFromPlaceHolderIdWithCache(ncm::RightsId *out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr) {
|
||||
AMS_ASSERT(m_interface != nullptr);
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id));
|
||||
|
||||
const auto vers = hos::GetVersion();
|
||||
if (vers >= hos::Version_16_0_0) {
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id, attr));
|
||||
} else {
|
||||
R_RETURN(m_interface->GetRightsIdFromPlaceHolderIdWithCacheDeprecated(out_rights_id, cache_content_id, placeholder_id));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <stratosphere/ncm/ncm_i_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_i_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_memory_report.hpp>
|
||||
#include <stratosphere/fs/fs_content_storage_id.hpp>
|
||||
|
||||
#define AMS_NCM_I_CONTENT_MANAGER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, CreateContentStorage, (ncm::StorageId storage_id), (storage_id)) \
|
||||
@@ -33,6 +34,7 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, ActivateContentMetaDatabase, (ncm::StorageId storage_id), (storage_id), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, InactivateContentMetaDatabase, (ncm::StorageId storage_id), (storage_id), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, InvalidateRightsIdCache, (), (), hos::Version_9_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetMemoryReport, (sf::Out<ncm::MemoryReport> out), (out), hos::Version_10_0_0)
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetMemoryReport, (sf::Out<ncm::MemoryReport> out), (out), hos::Version_10_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, ActivateFsContentStorage, (fs::ContentStorageId fs_storage_id), (fs_storage_id)) /* Technically min 16.0.0, but used. */
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::ncm, IContentManager, AMS_NCM_I_CONTENT_MANAGER_INTERFACE_INFO, 0xFDB4FFE1);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, HasAll, (sf::Out<bool> out, const sf::InArray<ncm::ContentMetaKey> &keys), (out, keys)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, GetSize, (sf::Out<u64> out_size, const ncm::ContentMetaKey &key), (out_size, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, GetRequiredSystemVersion, (sf::Out<u32> out_version, const ncm::ContentMetaKey &key), (out_version, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetPatchId, (sf::Out<ncm::PatchId> out_patch_id, const ncm::ContentMetaKey &key), (out_patch_id, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetPatchContentMetaId, (sf::Out<u64> out_patch_id, const ncm::ContentMetaKey &key), (out_patch_id, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, DisableForcibly, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, LookupOrphanContent, (const sf::OutArray<bool> &out_orphaned, const sf::InArray<ncm::ContentId> &content_ids), (out_orphaned, content_ids)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, Commit, (), ()) \
|
||||
|
||||
@@ -20,38 +20,44 @@
|
||||
#include <stratosphere/ncm/ncm_path.hpp>
|
||||
#include <stratosphere/ncm/ncm_rights_id.hpp>
|
||||
|
||||
#define AMS_NCM_I_CONTENT_STORAGE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GeneratePlaceHolderId, (sf::Out<ncm::PlaceHolderId> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, CreatePlaceHolder, (ncm::PlaceHolderId placeholder_id, ncm::ContentId content_id, s64 size), (placeholder_id, content_id, size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, DeletePlaceHolder, (ncm::PlaceHolderId placeholder_id), (placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, HasPlaceHolder, (sf::Out<bool> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, WritePlaceHolder, (ncm::PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data), (placeholder_id, offset, data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, Register, (ncm::PlaceHolderId placeholder_id, ncm::ContentId content_id), (placeholder_id, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, Delete, (ncm::ContentId content_id), (content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, Has, (sf::Out<bool> out, ncm::ContentId content_id), (out, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, GetPath, (sf::Out<ncm::Path> out, ncm::ContentId content_id), (out, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, GetPlaceHolderPath, (sf::Out<ncm::Path> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, CleanupAllPlaceHolder, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, ListPlaceHolder, (sf::Out<s32> out_count, const sf::OutArray<ncm::PlaceHolderId> &out_buf), (out_count, out_buf)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetContentCount, (sf::Out<s32> out_count), (out_count)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, ListContentId, (sf::Out<s32> out_count, const sf::OutArray<ncm::ContentId> &out_buf, s32 start_offset), (out_count, out_buf, start_offset)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetSizeFromContentId, (sf::Out<s64> out_size, ncm::ContentId content_id), (out_size, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, DisableForcibly, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 16, Result, RevertToPlaceHolder, (ncm::PlaceHolderId placeholder_id, ncm::ContentId old_content_id, ncm::ContentId new_content_id), (placeholder_id, old_content_id, new_content_id), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 17, Result, SetPlaceHolderSize, (ncm::PlaceHolderId placeholder_id, s64 size), (placeholder_id, size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 18, Result, ReadContentIdFile, (const sf::OutBuffer &buf, ncm::ContentId content_id, s64 offset), (buf, content_id, offset), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 19, Result, GetRightsIdFromPlaceHolderIdDeprecated, (sf::Out<ams::fs::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id), (out_rights_id, placeholder_id), hos::Version_2_0_0, hos::Version_2_3_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 19, Result, GetRightsIdFromPlaceHolderId, (sf::Out<ncm::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id), (out_rights_id, placeholder_id), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 20, Result, GetRightsIdFromContentIdDeprecated, (sf::Out<ams::fs::RightsId> out_rights_id, ncm::ContentId content_id), (out_rights_id, content_id), hos::Version_2_0_0, hos::Version_2_3_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 20, Result, GetRightsIdFromContentId, (sf::Out<ncm::RightsId> out_rights_id, ncm::ContentId content_id), (out_rights_id, content_id), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, WriteContentForDebug, (ncm::ContentId content_id, s64 offset, const sf::InBuffer &data), (content_id, offset, data), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 22, Result, GetFreeSpaceSize, (sf::Out<s64> out_size), (out_size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, GetTotalSpaceSize, (sf::Out<s64> out_size), (out_size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 24, Result, FlushPlaceHolder, (), (), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 25, Result, GetSizeFromPlaceHolderId, (sf::Out<s64> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 26, Result, RepairInvalidFileAttribute, (), (), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 27, Result, GetRightsIdFromPlaceHolderIdWithCache, (sf::Out<ncm::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id, ncm::ContentId cache_content_id), (out_rights_id, placeholder_id, cache_content_id), hos::Version_8_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 28, Result, RegisterPath, (const ncm::ContentId &content_id, const ncm::Path &path), (content_id, path), hos::Version_13_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 29, Result, ClearRegisteredPath, (), (), hos::Version_13_0_0)
|
||||
#define AMS_NCM_I_CONTENT_STORAGE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GeneratePlaceHolderId, (sf::Out<ncm::PlaceHolderId> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, CreatePlaceHolder_AtmosphereAlignmentFix, (ncm::ContentId content_id, ncm::PlaceHolderId placeholder_id, s64 size), (content_id, placeholder_id, size), hos::Version_Min, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, CreatePlaceHolder, (ncm::PlaceHolderId placeholder_id, ncm::ContentId content_id, s64 size), (placeholder_id, content_id, size), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, DeletePlaceHolder, (ncm::PlaceHolderId placeholder_id), (placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, HasPlaceHolder, (sf::Out<bool> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, WritePlaceHolder, (ncm::PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data), (placeholder_id, offset, data)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, Register_AtmosphereAlignmentFix, (ncm::ContentId content_id, ncm::PlaceHolderId placeholder_id), (content_id, placeholder_id), hos::Version_Min, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, Register, (ncm::PlaceHolderId placeholder_id, ncm::ContentId content_id), (placeholder_id, content_id), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, Delete, (ncm::ContentId content_id), (content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, Has, (sf::Out<bool> out, ncm::ContentId content_id), (out, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, GetPath, (sf::Out<ncm::Path> out, ncm::ContentId content_id), (out, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, GetPlaceHolderPath, (sf::Out<ncm::Path> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, CleanupAllPlaceHolder, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, ListPlaceHolder, (sf::Out<s32> out_count, const sf::OutArray<ncm::PlaceHolderId> &out_buf), (out_count, out_buf)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetContentCount, (sf::Out<s32> out_count), (out_count)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, ListContentId, (sf::Out<s32> out_count, const sf::OutArray<ncm::ContentId> &out_buf, s32 start_offset), (out_count, out_buf, start_offset)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, GetSizeFromContentId, (sf::Out<s64> out_size, ncm::ContentId content_id), (out_size, content_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, DisableForcibly, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 16, Result, RevertToPlaceHolder_AtmosphereAlignmentFix, (ncm::ContentId old_content_id, ncm::ContentId new_content_id, ncm::PlaceHolderId placeholder_id), (old_content_id, new_content_id, placeholder_id), hos::Version_2_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 16, Result, RevertToPlaceHolder, (ncm::PlaceHolderId placeholder_id, ncm::ContentId old_content_id, ncm::ContentId new_content_id), (placeholder_id, old_content_id, new_content_id), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 17, Result, SetPlaceHolderSize, (ncm::PlaceHolderId placeholder_id, s64 size), (placeholder_id, size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 18, Result, ReadContentIdFile, (const sf::OutBuffer &buf, ncm::ContentId content_id, s64 offset), (buf, content_id, offset), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 19, Result, GetRightsIdFromPlaceHolderIdDeprecated, (sf::Out<ams::fs::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id), (out_rights_id, placeholder_id), hos::Version_2_0_0, hos::Version_2_3_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 19, Result, GetRightsIdFromPlaceHolderIdDeprecated2, (sf::Out<ncm::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id), (out_rights_id, placeholder_id), hos::Version_3_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 19, Result, GetRightsIdFromPlaceHolderId, (sf::Out<ncm::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id, fs::ContentAttributes attr), (out_rights_id, placeholder_id, attr), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 20, Result, GetRightsIdFromContentIdDeprecated, (sf::Out<ams::fs::RightsId> out_rights_id, ncm::ContentId content_id), (out_rights_id, content_id), hos::Version_2_0_0, hos::Version_2_3_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 20, Result, GetRightsIdFromContentIdDeprecated2, (sf::Out<ncm::RightsId> out_rights_id, ncm::ContentId content_id), (out_rights_id, content_id), hos::Version_3_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 20, Result, GetRightsIdFromContentId, (sf::Out<ncm::RightsId> out_rights_id, ncm::ContentId content_id, fs::ContentAttributes attr), (out_rights_id, content_id, attr), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, WriteContentForDebug, (ncm::ContentId content_id, s64 offset, const sf::InBuffer &data), (content_id, offset, data), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 22, Result, GetFreeSpaceSize, (sf::Out<s64> out_size), (out_size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, GetTotalSpaceSize, (sf::Out<s64> out_size), (out_size), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 24, Result, FlushPlaceHolder, (), (), hos::Version_3_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 25, Result, GetSizeFromPlaceHolderId, (sf::Out<s64> out, ncm::PlaceHolderId placeholder_id), (out, placeholder_id), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 26, Result, RepairInvalidFileAttribute, (), (), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 27, Result, GetRightsIdFromPlaceHolderIdWithCacheDeprecated, (sf::Out<ncm::RightsId> out_rights_id, ncm::ContentId cache_content_id, ncm::PlaceHolderId placeholder_id), (out_rights_id, cache_content_id, placeholder_id), hos::Version_8_0_0, hos::Version_15_0_1) \
|
||||
AMS_SF_METHOD_INFO(C, H, 27, Result, GetRightsIdFromPlaceHolderIdWithCache, (sf::Out<ncm::RightsId> out_rights_id, ncm::PlaceHolderId placeholder_id, ncm::ContentId cache_content_id, fs::ContentAttributes attr), (out_rights_id, placeholder_id, cache_content_id, attr), hos::Version_16_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 28, Result, RegisterPath, (const ncm::ContentId &content_id, const ncm::Path &path), (content_id, path), hos::Version_13_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 29, Result, ClearRegisteredPath, (), (), hos::Version_13_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::ncm, IContentStorage, AMS_NCM_I_CONTENT_STORAGE_INTERFACE_INFO, 0xFEAE3DD1)
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace ams::ncm {
|
||||
|
||||
InstallContentInfo MakeInstallContentInfoFrom(const InstallContentMetaInfo &info, const PlaceHolderId &placeholder_id, util::optional<bool> is_temporary);
|
||||
|
||||
Result ReadContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const ContentMetaKey &key);
|
||||
Result ReadContentMetaInfoList(s32 *out_count, std::unique_ptr<ContentMetaInfo[]> *out_meta_infos, const ContentMetaKey &key, fs::ContentAttributes attr);
|
||||
Result ListRightsIdsByInstallContentMeta(s32 *out_count, Span<RightsId> out_span, const InstallContentMeta &content_meta, s32 offset);
|
||||
public:
|
||||
virtual Result CheckInstallable() { R_SUCCEED(); }
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ncm/ncm_i_content_meta_database.hpp>
|
||||
#include <stratosphere/ncm/ncm_integrated_list.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
class IntegratedContentMetaDatabaseImpl {
|
||||
NON_COPYABLE(IntegratedContentMetaDatabaseImpl);
|
||||
NON_MOVEABLE(IntegratedContentMetaDatabaseImpl);
|
||||
private:
|
||||
using ListType = ncm::IntegratedList<ncm::IContentMetaDatabase, 2>;
|
||||
using DataType = ListType::ListData;
|
||||
private:
|
||||
os::SdkRecursiveMutex m_mutex;
|
||||
ListType m_list;
|
||||
bool m_disabled;
|
||||
public:
|
||||
IntegratedContentMetaDatabaseImpl() : m_mutex(), m_list(), m_disabled(false) { /* ... */ }
|
||||
|
||||
void Add(sf::SharedPointer<ncm::IContentMetaDatabase> p, u8 id) {
|
||||
DataType data = {std::move(p), id};
|
||||
m_list.Add(data);
|
||||
}
|
||||
private:
|
||||
/* Helpers. */
|
||||
Result EnsureEnabled() const {
|
||||
R_UNLESS(!m_disabled, ncm::ResultInvalidContentMetaDatabase());
|
||||
R_SUCCEED();
|
||||
}
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result Set(const ContentMetaKey &key, const sf::InBuffer &value);
|
||||
Result Get(sf::Out<u64> out_size, const ContentMetaKey &key, const sf::OutBuffer &out_value);
|
||||
Result Remove(const ContentMetaKey &key);
|
||||
Result GetContentIdByType(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type);
|
||||
Result ListContentInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentInfo> &out_info, const ContentMetaKey &key, s32 offset);
|
||||
Result List(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaKey> &out_info, ContentMetaType meta_type, ApplicationId application_id, u64 min, u64 max, ContentInstallType install_type);
|
||||
Result GetLatestContentMetaKey(sf::Out<ContentMetaKey> out_key, u64 id);
|
||||
Result ListApplication(sf::Out<s32> out_entries_total, sf::Out<s32> out_entries_written, const sf::OutArray<ApplicationContentMetaKey> &out_keys, ContentMetaType meta_type);
|
||||
Result Has(sf::Out<bool> out, const ContentMetaKey &key);
|
||||
Result HasAll(sf::Out<bool> out, const sf::InArray<ContentMetaKey> &keys);
|
||||
Result GetSize(sf::Out<u64> out_size, const ContentMetaKey &key);
|
||||
Result GetRequiredSystemVersion(sf::Out<u32> out_version, const ContentMetaKey &key);
|
||||
Result GetPatchContentMetaId(sf::Out<u64> out_patch_id, const ContentMetaKey &key);
|
||||
Result DisableForcibly();
|
||||
Result LookupOrphanContent(const sf::OutArray<bool> &out_orphaned, const sf::InArray<ContentId> &content_ids);
|
||||
Result Commit();
|
||||
Result HasContent(sf::Out<bool> out, const ContentMetaKey &key, const ContentId &content_id);
|
||||
Result ListContentMetaInfo(sf::Out<s32> out_entries_written, const sf::OutArray<ContentMetaInfo> &out_meta_info, const ContentMetaKey &key, s32 offset);
|
||||
Result GetAttributes(sf::Out<u8> out_attributes, const ContentMetaKey &key);
|
||||
Result GetRequiredApplicationVersion(sf::Out<u32> out_version, const ContentMetaKey &key);
|
||||
Result GetContentIdByTypeAndIdOffset(sf::Out<ContentId> out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset);
|
||||
Result GetCount(sf::Out<u32> out_count);
|
||||
Result GetOwnerApplicationId(sf::Out<ApplicationId> out_id, const ContentMetaKey &key);
|
||||
Result GetContentAccessibilities(sf::Out<u8> out_accessibilities, const ContentMetaKey &key);
|
||||
Result GetContentInfoByType(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type);
|
||||
Result GetContentInfoByTypeAndIdOffset(sf::Out<ContentInfo> out_content_info, const ContentMetaKey &key, ContentType type, u8 id_offset);
|
||||
};
|
||||
static_assert(ncm::IsIContentMetaDatabase<IntegratedContentMetaDatabaseImpl>);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/ncm/ncm_i_content_storage.hpp>
|
||||
#include <stratosphere/ncm/ncm_integrated_list.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
class IntegratedContentStorageImpl {
|
||||
NON_COPYABLE(IntegratedContentStorageImpl);
|
||||
NON_MOVEABLE(IntegratedContentStorageImpl);
|
||||
private:
|
||||
using ListType = ncm::IntegratedList<ncm::IContentStorage, 2>;
|
||||
using DataType = ListType::ListData;
|
||||
private:
|
||||
os::SdkRecursiveMutex m_mutex;
|
||||
ListType m_list;
|
||||
bool m_disabled;
|
||||
public:
|
||||
IntegratedContentStorageImpl() : m_mutex(), m_list(), m_disabled(false) { /* ... */ }
|
||||
|
||||
void Add(sf::SharedPointer<ncm::IContentStorage> p, u8 id) {
|
||||
DataType data = {std::move(p), id};
|
||||
m_list.Add(data);
|
||||
}
|
||||
private:
|
||||
/* Helpers. */
|
||||
Result EnsureEnabled() const {
|
||||
R_UNLESS(!m_disabled, ncm::ResultInvalidContentStorage());
|
||||
R_SUCCEED();
|
||||
}
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result GeneratePlaceHolderId(sf::Out<PlaceHolderId> out);
|
||||
Result CreatePlaceHolder(PlaceHolderId placeholder_id, ContentId content_id, s64 size);
|
||||
Result DeletePlaceHolder(PlaceHolderId placeholder_id);
|
||||
Result HasPlaceHolder(sf::Out<bool> out, PlaceHolderId placeholder_id);
|
||||
Result WritePlaceHolder(PlaceHolderId placeholder_id, s64 offset, const sf::InBuffer &data);
|
||||
Result Register(PlaceHolderId placeholder_id, ContentId content_id);
|
||||
Result Delete(ContentId content_id);
|
||||
Result Has(sf::Out<bool> out, ContentId content_id);
|
||||
Result GetPath(sf::Out<Path> out, ContentId content_id);
|
||||
Result GetPlaceHolderPath(sf::Out<Path> out, PlaceHolderId placeholder_id);
|
||||
Result CleanupAllPlaceHolder();
|
||||
Result ListPlaceHolder(sf::Out<s32> out_count, const sf::OutArray<PlaceHolderId> &out_buf);
|
||||
Result GetContentCount(sf::Out<s32> out_count);
|
||||
Result ListContentId(sf::Out<s32> out_count, const sf::OutArray<ContentId> &out_buf, s32 start_offset);
|
||||
Result GetSizeFromContentId(sf::Out<s64> out_size, ContentId content_id);
|
||||
Result DisableForcibly();
|
||||
Result RevertToPlaceHolder(PlaceHolderId placeholder_id, ContentId old_content_id, ContentId new_content_id);
|
||||
Result SetPlaceHolderSize(PlaceHolderId placeholder_id, s64 size);
|
||||
Result ReadContentIdFile(const sf::OutBuffer &buf, ContentId content_id, s64 offset);
|
||||
Result GetRightsIdFromPlaceHolderIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, PlaceHolderId placeholder_id);
|
||||
Result GetRightsIdFromPlaceHolderIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id);
|
||||
Result GetRightsIdFromPlaceHolderId(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, fs::ContentAttributes attr);
|
||||
Result GetRightsIdFromContentIdDeprecated(sf::Out<ams::fs::RightsId> out_rights_id, ContentId content_id);
|
||||
Result GetRightsIdFromContentIdDeprecated2(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id);
|
||||
Result GetRightsIdFromContentId(sf::Out<ncm::RightsId> out_rights_id, ContentId content_id, fs::ContentAttributes attr);
|
||||
Result WriteContentForDebug(ContentId content_id, s64 offset, const sf::InBuffer &data);
|
||||
Result GetFreeSpaceSize(sf::Out<s64> out_size);
|
||||
Result GetTotalSpaceSize(sf::Out<s64> out_size);
|
||||
Result FlushPlaceHolder();
|
||||
Result GetSizeFromPlaceHolderId(sf::Out<s64> out, PlaceHolderId placeholder_id);
|
||||
Result RepairInvalidFileAttribute();
|
||||
Result GetRightsIdFromPlaceHolderIdWithCache(sf::Out<ncm::RightsId> out_rights_id, PlaceHolderId placeholder_id, ContentId cache_content_id, fs::ContentAttributes attr);
|
||||
Result RegisterPath(const ContentId &content_id, const Path &path);
|
||||
Result ClearRegisteredPath();
|
||||
|
||||
/* 16.0.0 Alignment change hacks. */
|
||||
Result CreatePlaceHolder_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id, s64 size) { R_RETURN(this->CreatePlaceHolder(placeholder_id, content_id, size)); }
|
||||
Result Register_AtmosphereAlignmentFix(ContentId content_id, PlaceHolderId placeholder_id) { R_RETURN(this->Register(placeholder_id, content_id)); }
|
||||
Result RevertToPlaceHolder_AtmosphereAlignmentFix(ncm::ContentId old_content_id, ncm::ContentId new_content_id, ncm::PlaceHolderId placeholder_id) { R_RETURN(this->RevertToPlaceHolder(placeholder_id, old_content_id, new_content_id)); }
|
||||
Result GetRightsIdFromPlaceHolderIdWithCacheDeprecated(sf::Out<ncm::RightsId> out_rights_id, ContentId cache_content_id, PlaceHolderId placeholder_id) { R_RETURN(this->GetRightsIdFromPlaceHolderIdWithCache(out_rights_id, placeholder_id, cache_content_id, fs::ContentAttributes_None)); }
|
||||
};
|
||||
static_assert(ncm::IsIContentStorage<IntegratedContentStorageImpl>);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/sf/sf_shared_object.hpp>
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
template<typename T, size_t N>
|
||||
class IntegratedList {
|
||||
static_assert(N <= std::numeric_limits<u8>::max());
|
||||
public:
|
||||
struct ListData {
|
||||
sf::SharedPointer<T> interface;
|
||||
u8 id;
|
||||
};
|
||||
private:
|
||||
size_t m_count;
|
||||
sf::SharedPointer<T> m_interfaces[N];
|
||||
u8 m_ids[N];
|
||||
public:
|
||||
IntegratedList() : m_count(0), m_interfaces(), m_ids() { /* ... */ }
|
||||
|
||||
void Add(ListData &data) {
|
||||
/* Find place to insert into the list. */
|
||||
const size_t pos = std::distance(std::begin(m_ids), std::lower_bound(std::begin(m_ids), std::end(m_ids), data.id));
|
||||
|
||||
/* If we need to, move stuff to make space. */
|
||||
if (m_ids[pos] > data.id) {
|
||||
AMS_ABORT_UNLESS(m_count < N);
|
||||
|
||||
for (size_t i = m_count; i > pos; --i) {
|
||||
m_ids[i] = std::move(m_ids[i - 1]);
|
||||
m_interfaces[i] = std::move(m_interfaces[i - 1]);
|
||||
}
|
||||
|
||||
/* If we're inserting somewhere in the middle, increment count. */
|
||||
m_count++;
|
||||
} else if (m_ids[pos] < data.id) {
|
||||
/* If we're inserting at the end, increment count. */
|
||||
AMS_ABORT_UNLESS(m_count < N);
|
||||
m_count++;
|
||||
}
|
||||
|
||||
/* Set at position. */
|
||||
m_interfaces[pos] = data.interface;
|
||||
m_ids[pos] = data.id;
|
||||
}
|
||||
|
||||
ListData Get(size_t idx) {
|
||||
AMS_ABORT_UNLESS(idx < m_count);
|
||||
|
||||
return { m_interfaces[idx], m_ids[idx] };
|
||||
}
|
||||
|
||||
Result TryEach(auto callback) {
|
||||
Result result = ResultSuccess();
|
||||
for (size_t i = 0; i < m_count; ++i) {
|
||||
result = callback(this->Get(i));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
Result ForAll(auto callback) {
|
||||
for (size_t i = 0; i < m_count; ++i) {
|
||||
R_TRY(callback(this->Get(i)));
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t GetCount() const { return m_count; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ namespace ams::ncm {
|
||||
|
||||
constexpr inline s32 SystemMaxContentMetaCount = 0x800;
|
||||
constexpr inline s32 GameCardMaxContentMetaCount = 0x800;
|
||||
constexpr inline s32 HostMaxContentMetaCount = 0x800;
|
||||
constexpr inline s32 UserMaxContentMetaCount = 0x2000;
|
||||
constexpr inline s32 SdCardMaxContentMetaCount = 0x2000;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::ncm {
|
||||
|
||||
struct alignas(8) PlaceHolderId {
|
||||
struct PlaceHolderId {
|
||||
util::Uuid uuid;
|
||||
|
||||
bool operator==(const PlaceHolderId &other) const {
|
||||
@@ -38,7 +38,7 @@ namespace ams::ncm {
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(alignof(PlaceHolderId) == 8);
|
||||
static_assert(alignof(PlaceHolderId) == 1);
|
||||
|
||||
constexpr inline PlaceHolderId InvalidPlaceHolderId = { util::InvalidUuid };
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user