Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8274081e39 | ||
|
|
f1ad26ce84 | ||
|
|
e4c314146e | ||
|
|
52f00731d9 | ||
|
|
476d658a79 | ||
|
|
7263022bac | ||
|
|
e0e7aa1e2f | ||
|
|
bd9d8fff46 | ||
|
|
61e3f0b391 | ||
|
|
cd9b173318 | ||
|
|
a8df400825 | ||
|
|
68040e2922 | ||
|
|
8da4d14e15 | ||
|
|
e2ebf9c0ff | ||
|
|
5fb6f52b9e | ||
|
|
982389dceb | ||
|
|
f636596ee2 | ||
|
|
0a2440522f | ||
|
|
3292ea5970 | ||
|
|
33d42f4831 | ||
|
|
46094cfb3e | ||
|
|
618691a500 | ||
|
|
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 | ||
|
|
36a33617a4 | ||
|
|
8ce4f19615 | ||
|
|
05fee5d53c | ||
|
|
f88606fbc1 | ||
|
|
e580c3970c | ||
|
|
2603136e8b | ||
|
|
3d3f8629af | ||
|
|
d178ea368a | ||
|
|
b444385762 | ||
|
|
0c3f1bed33 | ||
|
|
bf4fdf6188 | ||
|
|
13238fc4fd | ||
|
|
e7a1e0fee2 | ||
|
|
5a918f3bc9 | ||
|
|
ea82889e6c | ||
|
|
7f2cbba543 | ||
|
|
5ee7d8a5ed | ||
|
|
947fdcf6f6 | ||
|
|
2e73f33eb0 | ||
|
|
752704a798 | ||
|
|
d00406e089 | ||
|
|
dddb5bfdea | ||
|
|
ed0572bd53 | ||
|
|
b3bbc99cfb | ||
|
|
0ebbbc839f | ||
|
|
275c0234cb | ||
|
|
427130a122 | ||
|
|
ef07964ea0 | ||
|
|
e1aff401ba | ||
|
|
05de5538d6 | ||
|
|
9929517a83 | ||
|
|
545765d167 | ||
|
|
8d9e3f2a31 | ||
|
|
28f0940c58 | ||
|
|
d954e85826 | ||
|
|
7e6b369605 | ||
|
|
e702eab21c | ||
|
|
592b9c66d1 | ||
|
|
8e17feff94 | ||
|
|
6514e365ad |
@@ -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.
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
; Note that this setting is ignored (and treated as 1) when htc is enabled.
|
||||
; 0 = Disabled, 1 = Enabled
|
||||
; enable_log_manager = u8!0x0
|
||||
; Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs)
|
||||
; NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database.
|
||||
; 0 = Disabled, 1 = Enabled
|
||||
; enable_external_bluetooth_db = u8!0x0
|
||||
[hbloader]
|
||||
; Controls the size of the homebrew heap when running as applet.
|
||||
; If set to zero, all available applet memory is used as heap.
|
||||
|
||||
@@ -1,4 +1,54 @@
|
||||
# Changelog
|
||||
## 1.5.1
|
||||
+ `fatal` was updated to reduce memory footprint.
|
||||
+ Starting in 16.0.0, official `fatal` has no framebuffer or rendering logic, and instead calls other system service commands to draw the screen.
|
||||
+ However, these commands aren't usable by atmosphère (too small rendering window, bad color support).
|
||||
+ To reduce the relative memory footprint differential between atmosphère and official code, the framebuffer (2 MB) is now dynamically allocated when needed.
|
||||
+ This will try to allocate from multiple pools (preferring System > System_NonSecure > Application).
|
||||
+ This technically requires that 2 MB be available in at least one of these pools for the fatal screen to render (otherwise, a reboot-to-black-and-white-fatal will occur), but this should be a non-issue in almost all cases.
|
||||
+ A feature was added to optionally mirror the bluetooth pairing database to the SD card (thanks @ndeadly).
|
||||
+ This allows device pairings to be automatically kept in-sync across sysmmc/all emummcs.
|
||||
+ This is opt-in, and can be controlled by setting `atmosphere!enable_external_bluetooth_db = u8!0x1`.
|
||||
+ When enabled, the pairing database will be synchronized to `/atmosphere/bluetooth_devices.db`.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 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 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.
|
||||
+ dmnt.gen2's gdbstub now sanitizes thread names to prevent invalid characters from breaking gdb.
|
||||
+ dmnt.gen2's gdbstub now reports the architecture tag correctly when attached to 32-bit processes.
|
||||
+ Support for program-specific html manual content overrides was added for non-hbl takeover context.
|
||||
+ A bug was fixed in how emummc constructed the alternate Nintendo directory path.
|
||||
+ Previously, this was using `/*/Nintendo/Nintendo` instead of `/*/Nintendo`.
|
||||
+ Code was added to automatically move the old folders to the new ones when booting into emummc.
|
||||
+ A bug was fixed in boot that caused an incorrectly low input voltage limit to be set.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 1.3.2
|
||||
+ Support was improved for 14.0.0+.
|
||||
+ `loader` was updated to reflect the latest official behaviors.
|
||||
|
||||
4
emummc/.gitrepo
vendored
4
emummc/.gitrepo
vendored
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/m4xw/emummc
|
||||
branch = develop
|
||||
commit = 56a2e8a2078944d9bf8daead237036254bb6e36d
|
||||
parent = 4f763b2aa401ac3e3d699ec8c866ae9b3c8fb28d
|
||||
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 - 13.1.0**
|
||||
**1.0.0 - 16.0.0**
|
||||
|
||||
## Features
|
||||
* Arbitrary SDMMC backend selection
|
||||
|
||||
16
emummc/source/FS/FS_offsets.c
vendored
16
emummc/source/FS/FS_offsets.c
vendored
@@ -61,6 +61,10 @@
|
||||
#include "offsets/1310_exfat.h"
|
||||
#include "offsets/1400.h"
|
||||
#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
|
||||
@@ -133,6 +137,10 @@ DEFINE_OFFSET_STRUCT(_1310);
|
||||
DEFINE_OFFSET_STRUCT(_1310_EXFAT);
|
||||
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) {
|
||||
@@ -226,6 +234,14 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1400));
|
||||
case FS_VER_14_0_0_EXFAT:
|
||||
return &(GET_OFFSET_STRUCT_NAME(_1400_EXFAT));
|
||||
case FS_VER_15_0_0:
|
||||
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);
|
||||
}
|
||||
|
||||
6
emummc/source/FS/FS_versions.h
vendored
6
emummc/source/FS/FS_versions.h
vendored
@@ -89,6 +89,12 @@ enum FS_VER
|
||||
FS_VER_14_0_0,
|
||||
FS_VER_14_0_0_EXFAT,
|
||||
|
||||
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/1500.h
vendored
Normal file
59
emummc/source/FS/offsets/1500.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_1500_H__
|
||||
#define __FS_1500_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1500_SDMMC_ACCESSOR_GC 0x183E20
|
||||
#define FS_OFFSET_1500_SDMMC_ACCESSOR_SD 0x185AA0
|
||||
#define FS_OFFSET_1500_SDMMC_ACCESSOR_NAND 0x1842E0
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1500_SDMMC_WRAPPER_READ 0x17FCF0
|
||||
#define FS_OFFSET_1500_SDMMC_WRAPPER_WRITE 0x17FD50
|
||||
#define FS_OFFSET_1500_RTLD 0x26518
|
||||
#define FS_OFFSET_1500_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1500_CLKRST_SET_MIN_V_CLK_RATE 0x1A0870
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1500_LOCK_MUTEX 0x1791A0
|
||||
#define FS_OFFSET_1500_UNLOCK_MUTEX 0x1791F0
|
||||
|
||||
#define FS_OFFSET_1500_SDMMC_WRAPPER_CONTROLLER_OPEN 0x17FCB0
|
||||
#define FS_OFFSET_1500_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x17FCD0
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1500_SD_MUTEX 0xFF33F0
|
||||
#define FS_OFFSET_1500_NAND_MUTEX 0xFEE2E8
|
||||
#define FS_OFFSET_1500_ACTIVE_PARTITION 0xFEE328
|
||||
#define FS_OFFSET_1500_SDMMC_DAS_HANDLE 0xFD38D8
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1500_SD_DAS_INIT 0x25454
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1500_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00063050, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006FDE8, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x000768D4, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00089364, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1500_H__
|
||||
59
emummc/source/FS/offsets/1500_exfat.h
vendored
Normal file
59
emummc/source/FS/offsets/1500_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_1500_EXFAT_H__
|
||||
#define __FS_1500_EXFAT_H__
|
||||
|
||||
// Accessor vtable getters
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_ACCESSOR_GC 0x18EDB0
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_ACCESSOR_SD 0x190A30
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_ACCESSOR_NAND 0x18F270
|
||||
|
||||
// Hooks
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_WRAPPER_READ 0x18AC80
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_WRAPPER_WRITE 0x18ACE0
|
||||
#define FS_OFFSET_1500_EXFAT_RTLD 0x26518
|
||||
#define FS_OFFSET_1500_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||
|
||||
#define FS_OFFSET_1500_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1AB800
|
||||
|
||||
// Misc funcs
|
||||
#define FS_OFFSET_1500_EXFAT_LOCK_MUTEX 0x184130
|
||||
#define FS_OFFSET_1500_EXFAT_UNLOCK_MUTEX 0x184180
|
||||
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x18AC40
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x18AC60
|
||||
|
||||
// Misc Data
|
||||
#define FS_OFFSET_1500_EXFAT_SD_MUTEX 0x10053F0
|
||||
#define FS_OFFSET_1500_EXFAT_NAND_MUTEX 0x10002E8
|
||||
#define FS_OFFSET_1500_EXFAT_ACTIVE_PARTITION 0x1000328
|
||||
#define FS_OFFSET_1500_EXFAT_SDMMC_DAS_HANDLE 0xFE08D8
|
||||
|
||||
// NOPs
|
||||
#define FS_OFFSET_1500_EXFAT_SD_DAS_INIT 0x25454
|
||||
|
||||
// Nintendo Paths
|
||||
#define FS_OFFSET_1500_EXFAT_NINTENDO_PATHS \
|
||||
{ \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x00063050, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 3, .adrp_offset = 0x0006FDE8, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x000768D4, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 4, .adrp_offset = 0x00089364, .add_rel_offset = 0x00000004}, \
|
||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||
}
|
||||
|
||||
#endif // __FS_1500_EXFAT_H__
|
||||
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 0xEC, 0x5E, 0xB5, 0x11, 0xD5, 0x43, 0x1E, 0x6A, 0x4E, 0x54, 0x6F, 0xD4, 0xD3, 0x22, 0xCE, 0x87
|
||||
.byte 0x3A, 0x9C, 0xF0, 0x39, 0x70, 0x23, 0xF6, 0xAF, 0x71, 0x44, 0x60, 0xF4, 0x6D, 0xED, 0xA1, 0xD6
|
||||
|
||||
/* Mariko Production Master Kek Source. */
|
||||
.byte 0xD2, 0x68, 0xC6, 0x53, 0x9D, 0x94, 0xF9, 0xA8, 0xA5, 0xA8, 0xA7, 0xC8, 0x8F, 0x53, 0x4B, 0x7A
|
||||
.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. */
|
||||
@@ -105,6 +105,8 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C /* Master key 0A encrypted with Master key 0B. */
|
||||
.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. */
|
||||
@@ -121,6 +123,8 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 /* Master key 0A encrypted with Master key 0B. */
|
||||
.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. */
|
||||
@@ -134,6 +138,8 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6 /* 12.1.0 Device Master Key Source Source. */
|
||||
.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. */
|
||||
@@ -147,6 +153,8 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xC4, 0xBB, 0xF3, 0x9F, 0xA3, 0xAA, 0x00, 0x99, 0x7C, 0x97, 0xAD, 0x91, 0x8F, 0xE8, 0x45, 0xCB /* 12.1.0 Device Master Kek Source. */
|
||||
.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. */
|
||||
@@ -160,3 +168,5 @@ _ZN3ams6secmon4boot15VolatileKeyDataE:
|
||||
.byte 0xC2, 0x65, 0x34, 0x6E, 0xC7, 0xC6, 0x5D, 0x97, 0x3E, 0x34, 0x5C, 0x6B, 0xB3, 0x7E, 0xC6, 0xE3 /* 12.1.0 Device Master Kek Source. */
|
||||
.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 == 14);
|
||||
static_assert(pkg1::KeyGeneration_Count == 16);
|
||||
if (key_generation >= pkg1::KeyGeneration_Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace ams::secmon::smc {
|
||||
[fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB,
|
||||
[fuse::DramId_HoagHynix1y4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_AulaHynix1y4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_Deprecated7] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung8GB] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_IowaHynix4GB] = pkg1::MemorySize_4GB,
|
||||
@@ -61,7 +61,7 @@ namespace ams::secmon::smc {
|
||||
[fuse::DramId_HoagSamsung8GB] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_HoagHynix4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagMicron4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_Deprecated16] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y4GBX] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y8GBX] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_HoagSamsung1y4GBX] = pkg1::MemorySize_4GB,
|
||||
@@ -74,6 +74,12 @@ namespace ams::secmon::smc {
|
||||
[fuse::DramId_HoagMicron1y4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_AulaMicron1y4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_AulaSamsung1y8GBX] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagX1X2Samsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_AulaX1X2Samsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagSamsung4GBY] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_AulaSamsung4GBY] = pkg1::MemorySize_4GB,
|
||||
};
|
||||
|
||||
constexpr const pkg1::MemoryMode MemoryModes[] = {
|
||||
|
||||
@@ -23,17 +23,17 @@ namespace ams::nxboot {
|
||||
|
||||
alignas(se::AesBlockSize) constexpr inline const u8 MarikoMasterKekSource[se::AesBlockSize] = {
|
||||
/* TODO: Update on next change of keys. */
|
||||
0xD2, 0x68, 0xC6, 0x53, 0x9D, 0x94, 0xF9, 0xA8, 0xA5, 0xA8, 0xA7, 0xC8, 0x8F, 0x53, 0x4B, 0x7A
|
||||
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. */
|
||||
0xEC, 0x5E, 0xB5, 0x11, 0xD5, 0x43, 0x1E, 0x6A, 0x4E, 0x54, 0x6F, 0xD4, 0xD3, 0x22, 0xCE, 0x87
|
||||
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. */
|
||||
0xF0, 0x13, 0x37, 0x9A, 0xD5, 0x63, 0x51, 0xC3, 0xB4, 0x96, 0x35, 0xBC, 0x9C, 0xE8, 0x76, 0x81
|
||||
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] = {
|
||||
@@ -68,6 +68,8 @@ namespace ams::nxboot {
|
||||
{ 0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6 }, /* 12.1.0 Device Master Key Source Source. */
|
||||
{ 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] = {
|
||||
@@ -82,6 +84,8 @@ namespace ams::nxboot {
|
||||
{ 0xC2, 0x65, 0x34, 0x6E, 0xC7, 0xC6, 0x5D, 0x97, 0x3E, 0x34, 0x5C, 0x6B, 0xB3, 0x7E, 0xC6, 0xE3 }, /* 12.1.0 Device Master Kek Source. */
|
||||
{ 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] = {
|
||||
@@ -96,6 +100,8 @@ namespace ams::nxboot {
|
||||
{ 0xC4, 0xBB, 0xF3, 0x9F, 0xA3, 0xAA, 0x00, 0x99, 0x7C, 0x97, 0xAD, 0x91, 0x8F, 0xE8, 0x45, 0xCB }, /* 12.1.0 Device Master Kek Source. */
|
||||
{ 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] = {
|
||||
@@ -113,6 +119,8 @@ namespace ams::nxboot {
|
||||
{ 0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 }, /* Master key 0A encrypted with Master key 0B. */
|
||||
{ 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] = {
|
||||
@@ -130,6 +138,8 @@ namespace ams::nxboot {
|
||||
{ 0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C }, /* Master key 0A encrypted with Master key 0B. */
|
||||
{ 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 == 14);
|
||||
static_assert(pkg1::KeyGeneration_Count == 16);
|
||||
if (key_generation >= pkg1::KeyGeneration_Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,10 @@ namespace ams::nxboot {
|
||||
return ams::TargetFirmware_13_2_1;
|
||||
} else if (std::memcmp(package1 + 0x10, "20220209", 8) == 0) {
|
||||
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:
|
||||
|
||||
@@ -156,6 +156,12 @@ namespace ams::nxboot {
|
||||
FsVersion_14_0_0,
|
||||
FsVersion_14_0_0_Exfat,
|
||||
|
||||
FsVersion_15_0_0,
|
||||
FsVersion_15_0_0_Exfat,
|
||||
|
||||
FsVersion_16_0_0,
|
||||
FsVersion_16_0_0_Exfat,
|
||||
|
||||
FsVersion_Count,
|
||||
};
|
||||
|
||||
@@ -227,6 +233,12 @@ 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) {
|
||||
@@ -628,6 +640,22 @@ namespace ams::nxboot {
|
||||
AddPatch(fs_meta, 0x195769, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x16F6B0, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
case FsVersion_15_0_0:
|
||||
AddPatch(fs_meta, 0x184259, NogcPatch0, sizeof(NogcPatch0));
|
||||
AddPatch(fs_meta, 0x15EDE4, NogcPatch1, sizeof(NogcPatch1));
|
||||
break;
|
||||
case FsVersion_15_0_0_Exfat:
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,6 @@ namespace ams::nxboot {
|
||||
HANDLE_DRAM_CASE( 3, 12)
|
||||
HANDLE_DRAM_CASE( 5, 12)
|
||||
HANDLE_DRAM_CASE( 6, 12)
|
||||
HANDLE_DRAM_CASE( 7, 0)
|
||||
HANDLE_DRAM_CASE( 8, 1)
|
||||
HANDLE_DRAM_CASE( 9, 2)
|
||||
HANDLE_DRAM_CASE(10, 3)
|
||||
@@ -94,7 +93,6 @@ namespace ams::nxboot {
|
||||
HANDLE_DRAM_CASE(13, 2)
|
||||
HANDLE_DRAM_CASE(14, 3)
|
||||
HANDLE_DRAM_CASE(15, 4)
|
||||
HANDLE_DRAM_CASE(16, 5)
|
||||
HANDLE_DRAM_CASE(17, 6)
|
||||
HANDLE_DRAM_CASE(18, 7)
|
||||
HANDLE_DRAM_CASE(19, 6)
|
||||
@@ -107,6 +105,12 @@ namespace ams::nxboot {
|
||||
HANDLE_DRAM_CASE(26, 11)
|
||||
HANDLE_DRAM_CASE(27, 11)
|
||||
HANDLE_DRAM_CASE(28, 7)
|
||||
HANDLE_DRAM_CASE(29, 0)
|
||||
HANDLE_DRAM_CASE(30, 0)
|
||||
HANDLE_DRAM_CASE(31, 0)
|
||||
HANDLE_DRAM_CASE(32, 5)
|
||||
HANDLE_DRAM_CASE(33, 5)
|
||||
HANDLE_DRAM_CASE(34, 5)
|
||||
default:
|
||||
AMS_ABORT("Invalid DRAM id");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = b91294d3b9960eafef6d5d80b08870d427324bc9
|
||||
parent = 3545c0aac2cdfc1f6f897e8c669a8e33358b3ece
|
||||
commit = b1607dc8a3d85bc8c859c60d70ebb4a3dcbb85b8
|
||||
parent = f1ad26ce844ab7a63b948281b50c12e5d1dac8ce
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_WINDOWS
|
||||
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
|
||||
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer -fno-data-sections
|
||||
export ATMOSPHERE_CFLAGS +=
|
||||
export ATMOSPHERE_CXXFLAGS +=
|
||||
export ATMOSPHERE_ASFLAGS +=
|
||||
|
||||
@@ -58,7 +58,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||
-Wl,--wrap,exit
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||
export CXXREQUIRED :=
|
||||
export CXXWRAPS := -Wl,--wrap,__p__acmdln
|
||||
export CXXWRAPS := -Wl,--wrap,__p__acmdln -Wl,--wrap,_set_invalid_parameter_handler
|
||||
else
|
||||
export CXXREQUIRED :=
|
||||
export CXXWRAPS :=
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace ams::fuse {
|
||||
DramId_IcosaSamsung6GB = 4,
|
||||
DramId_HoagHynix1y4GB = 5,
|
||||
DramId_AulaHynix1y4GB = 6,
|
||||
DramId_IowaX1X2Samsung4GB = 7,
|
||||
DramId_Deprecated7 = 7,
|
||||
DramId_IowaSansung4GB = 8,
|
||||
DramId_IowaSamsung8GB = 9,
|
||||
DramId_IowaHynix4GB = 10,
|
||||
@@ -64,7 +64,7 @@ namespace ams::fuse {
|
||||
DramId_HoagSamsung8GB = 13,
|
||||
DramId_HoagHynix4GB = 14,
|
||||
DramId_HoagMicron4GB = 15,
|
||||
DramId_IowaSamsung4GBY = 16,
|
||||
DramId_Deprecated16 = 16,
|
||||
DramId_IowaSamsung1y4GBX = 17,
|
||||
DramId_IowaSamsung1y8GBX = 18,
|
||||
DramId_HoagSamsung1y4GBX = 19,
|
||||
@@ -77,6 +77,12 @@ namespace ams::fuse {
|
||||
DramId_HoagMicron1y4GB = 26,
|
||||
DramId_AulaMicron1y4GB = 27,
|
||||
DramId_AulaSamsung1y8GBX = 28,
|
||||
DramId_IowaX1X2Samsung4GB = 29,
|
||||
DramId_HoagX1X2Samsung4GB = 30,
|
||||
DramId_AulaX1X2Samsung4GB = 31,
|
||||
DramId_IowaSamsung4GBY = 32,
|
||||
DramId_HoagSamsung4GBY = 33,
|
||||
DramId_AulaSamsung4GBY = 34,
|
||||
|
||||
DramId_Count,
|
||||
};
|
||||
@@ -119,4 +125,4 @@ namespace ams::fuse {
|
||||
bool IsOdmProductionMode();
|
||||
void ConfigureFuseBypass();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ams::pkg1 {
|
||||
enum MemoryArrange {
|
||||
MemoryArrange_Normal = 1,
|
||||
MemoryArrange_AppletDev = 2,
|
||||
MemoryArrange_SystemDev = 2,
|
||||
MemoryArrange_SystemDev = 3,
|
||||
};
|
||||
|
||||
enum MemoryMode {
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace ams::pkg1 {
|
||||
KeyGeneration_12_1_0 = 0x0B,
|
||||
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 0x15 in Nintendo's code. */
|
||||
constexpr inline int CurrentBootloaderVersion = 0x11;
|
||||
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'>;
|
||||
|
||||
@@ -39,12 +39,13 @@ namespace ams::fuse {
|
||||
struct OdmWord4 {
|
||||
using HardwareState1 = util::BitPack32::Field<0, 2, int>;
|
||||
using HardwareType1 = util::BitPack32::Field<HardwareState1::Next, 1, int>;
|
||||
using DramId = util::BitPack32::Field<HardwareType1::Next, 5, int>;
|
||||
using HardwareType2 = util::BitPack32::Field<DramId::Next, 1, int>;
|
||||
using DramId1 = util::BitPack32::Field<HardwareType1::Next, 5, int>;
|
||||
using HardwareType2 = util::BitPack32::Field<DramId1::Next, 1, int>;
|
||||
using HardwareState2 = util::BitPack32::Field<HardwareType2::Next, 1, int>;
|
||||
using RetailInteractiveDisplayState = util::BitPack32::Field<HardwareState2::Next, 1, int>;
|
||||
using FormatVersion = util::BitPack32::Field<RetailInteractiveDisplayState::Next, 1, int>;
|
||||
using Reserved = util::BitPack32::Field<FormatVersion::Next, 4, int>;
|
||||
using DramId2 = util::BitPack32::Field<FormatVersion::Next, 3, int>;
|
||||
using Reserved = util::BitPack32::Field<DramId2::Next, 1, int>;
|
||||
using HardwareType3 = util::BitPack32::Field<Reserved::Next, 4, int>;
|
||||
};
|
||||
|
||||
@@ -71,6 +72,15 @@ namespace ams::fuse {
|
||||
(odm_word4.Get<OdmWord4::HardwareType3>() << HardwareType3Shift);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE int GetDramIdValue(const util::BitPack32 odm_word4) {
|
||||
constexpr auto DramId1Shift = 0;
|
||||
constexpr auto DramId2Shift = OdmWord4::DramId1::Count + DramId1Shift;
|
||||
|
||||
|
||||
return (odm_word4.Get<OdmWord4::DramId1>() << DramId1Shift) |
|
||||
(odm_word4.Get<OdmWord4::DramId2>() << DramId2Shift);
|
||||
}
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFuses.GetAddress();
|
||||
|
||||
constinit bool g_checked_for_rcm_bug_patch = false;
|
||||
@@ -167,6 +177,8 @@ namespace ams::fuse {
|
||||
}
|
||||
|
||||
constexpr const TargetFirmware FuseVersionIncrementFirmwares[] = {
|
||||
TargetFirmware_16_0_0,
|
||||
TargetFirmware_15_0_0,
|
||||
TargetFirmware_13_2_1,
|
||||
TargetFirmware_12_0_2,
|
||||
TargetFirmware_11_0_0,
|
||||
@@ -305,7 +317,8 @@ namespace ams::fuse {
|
||||
}
|
||||
|
||||
DramId GetDramId() {
|
||||
return static_cast<DramId>(util::BitPack32{GetCommonOdmWord(4)}.Get<OdmWord4::DramId>());
|
||||
/* Get the value. */
|
||||
return static_cast<DramId>(GetDramIdValue(util::BitPack32{GetCommonOdmWord(4)}));
|
||||
}
|
||||
|
||||
HardwareType GetHardwareType() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
|
||||
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
|
||||
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit);
|
||||
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit);
|
||||
Result Finalize();
|
||||
private:
|
||||
Result MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll);
|
||||
@@ -208,7 +208,7 @@ namespace ams::kern::arch::arm64 {
|
||||
}
|
||||
}
|
||||
|
||||
Result MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, bool not_first, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result MapGroup(KProcessAddress virt_addr, const KPageGroup &pg, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll);
|
||||
|
||||
bool MergePages(KProcessAddress virt_addr, PageLinkedList *page_list);
|
||||
@@ -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; }
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace ams::kern::arch::arm64 {
|
||||
m_page_table.Activate(id);
|
||||
}
|
||||
|
||||
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) {
|
||||
R_RETURN(m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager, resource_limit));
|
||||
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit) {
|
||||
R_RETURN(m_page_table.InitializeForProcess(id, as_type, enable_aslr, enable_das_merge, from_back, pool, code_address, code_size, system_resource, resource_limit));
|
||||
}
|
||||
|
||||
void Finalize() { m_page_table.Finalize(); }
|
||||
@@ -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) {
|
||||
@@ -110,6 +110,14 @@ namespace ams::kern::arch::arm64 {
|
||||
R_RETURN(m_page_table.MapRegion(region_type, perm));
|
||||
}
|
||||
|
||||
Result MapInsecureMemory(KProcessAddress address, size_t size) {
|
||||
R_RETURN(m_page_table.MapInsecureMemory(address, size));
|
||||
}
|
||||
|
||||
Result UnmapInsecureMemory(KProcessAddress address, size_t size) {
|
||||
R_RETURN(m_page_table.UnmapInsecureMemory(address, size));
|
||||
}
|
||||
|
||||
Result MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) {
|
||||
R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm));
|
||||
}
|
||||
@@ -158,12 +166,12 @@ namespace ams::kern::arch::arm64 {
|
||||
R_RETURN(m_page_table.WriteDebugIoMemory(address, buffer, size));
|
||||
}
|
||||
|
||||
Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
|
||||
R_RETURN(m_page_table.LockForMapDeviceAddressSpace(address, size, perm, is_aligned));
|
||||
Result LockForMapDeviceAddressSpace(bool *out_is_io, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned, bool check_heap) {
|
||||
R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm, is_aligned, check_heap));
|
||||
}
|
||||
|
||||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size) {
|
||||
R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size));
|
||||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) {
|
||||
R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap));
|
||||
}
|
||||
|
||||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace ams::kern::board::generic {
|
||||
R_THROW(ams::kern::svc::ResultNotImplemented());
|
||||
}
|
||||
|
||||
Result ALWAYS_INLINE Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
|
||||
MESOSPHERE_UNUSED(page_table, process_address, size, device_address, device_perm, is_aligned);
|
||||
Result ALWAYS_INLINE Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool is_io) {
|
||||
MESOSPHERE_UNUSED(page_table, process_address, size, device_address, device_perm, is_aligned, is_io);
|
||||
R_THROW(ams::kern::svc::ResultNotImplemented());
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size);
|
||||
Result Detach(ams::svc::DeviceName device_name);
|
||||
|
||||
Result Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned);
|
||||
Result Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool is_io);
|
||||
Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address);
|
||||
|
||||
void Unmap(KDeviceVirtualAddress device_address, size_t size) {
|
||||
|
||||
@@ -20,14 +20,20 @@
|
||||
namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
class KSystemControl : public KSystemControlBase {
|
||||
public:
|
||||
/* This can be overridden as needed. */
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,13 +36,13 @@ namespace ams::kern::init {
|
||||
size_t num_KDebug;
|
||||
size_t num_KIoPool;
|
||||
size_t num_KIoRegion;
|
||||
size_t num_KSessionRequestMappings;
|
||||
};
|
||||
|
||||
NOINLINE void InitializeSlabResourceCounts();
|
||||
const KSlabResourceCounts &GetSlabResourceCounts();
|
||||
|
||||
size_t CalculateTotalSlabHeapSize();
|
||||
NOINLINE void InitializeKPageBufferSlabHeap();
|
||||
NOINLINE void InitializeSlabHeaps();
|
||||
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace ams::kern {
|
||||
|
||||
class KAutoObject;
|
||||
|
||||
class KSystemResource;
|
||||
|
||||
#define FOR_EACH_K_CLASS_TOKEN_OBJECT_TYPE(HANDLER) \
|
||||
HANDLER(KAutoObject) \
|
||||
\
|
||||
@@ -48,7 +50,8 @@ namespace ams::kern {
|
||||
HANDLER(KSessionRequest) \
|
||||
HANDLER(KCodeMemory) \
|
||||
HANDLER(KIoPool) \
|
||||
HANDLER(KIoRegion)
|
||||
HANDLER(KIoRegion) \
|
||||
HANDLER(KSystemResource)
|
||||
|
||||
class KClassTokenGenerator {
|
||||
public:
|
||||
@@ -95,7 +98,7 @@ namespace ams::kern {
|
||||
if constexpr (std::is_same<T, KAutoObject>::value) {
|
||||
static_assert(T::ObjectType == ObjectType::KAutoObject);
|
||||
return 0;
|
||||
} else if constexpr (!std::is_final<T>::value) {
|
||||
} else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
|
||||
static_assert(ObjectType::BaseClassesStart <= T::ObjectType && T::ObjectType < ObjectType::BaseClassesEnd);
|
||||
constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) - static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
|
||||
return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
|
||||
@@ -142,6 +145,12 @@ namespace ams::kern {
|
||||
KIoPool,
|
||||
KIoRegion,
|
||||
|
||||
/* NOTE: What occupies these gaps? They can be inferred, but they don't make sense. */
|
||||
KAlpha,
|
||||
KBeta,
|
||||
|
||||
KSystemResource,
|
||||
|
||||
FinalClassesLast,
|
||||
|
||||
FinalClassesEnd = FinalClassesStart + NumFinalClasses,
|
||||
@@ -178,8 +187,10 @@ namespace ams::kern {
|
||||
}
|
||||
|
||||
for (auto fin = util::ToUnderlying(KClassTokenGenerator::ObjectType::FinalClassesStart); fin < util::ToUnderlying(KClassTokenGenerator::ObjectType::FinalClassesLast); ++fin) {
|
||||
if (!IsObjectTypeIncludedByMacro(static_cast<KClassTokenGenerator::ObjectType>(fin))) {
|
||||
return false;
|
||||
if (const auto o = static_cast<KClassTokenGenerator::ObjectType>(fin); !IsObjectTypeIncludedByMacro(o)) {
|
||||
if (o != KClassTokenGenerator::ObjectType::KAlpha && o != KClassTokenGenerator::ObjectType::KBeta) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -41,17 +41,17 @@ namespace ams::kern {
|
||||
Result Attach(ams::svc::DeviceName device_name);
|
||||
Result Detach(ams::svc::DeviceName device_name);
|
||||
|
||||
Result MapByForce(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm) {
|
||||
R_RETURN(this->Map(page_table, process_address, size, device_address, device_perm, false));
|
||||
Result MapByForce(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, u32 option) {
|
||||
R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
|
||||
}
|
||||
|
||||
Result MapAligned(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm) {
|
||||
R_RETURN(this->Map(page_table, process_address, size, device_address, device_perm, true));
|
||||
Result MapAligned(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, u32 option) {
|
||||
R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
|
||||
}
|
||||
|
||||
Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address);
|
||||
private:
|
||||
Result Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned);
|
||||
Result Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, u32 option, bool is_aligned);
|
||||
public:
|
||||
static void Initialize();
|
||||
};
|
||||
|
||||
@@ -38,30 +38,37 @@ namespace ams::kern {
|
||||
size_t m_peak;
|
||||
size_t m_count;
|
||||
KVirtualAddress m_address;
|
||||
KVirtualAddress m_aligned_address;
|
||||
size_t m_size;
|
||||
public:
|
||||
KDynamicPageManager() : m_lock(), m_page_bitmap(), m_used(), m_peak(), m_count(), m_address(Null<KVirtualAddress>), m_size() { /* ... */ }
|
||||
KDynamicPageManager() : m_lock(), m_page_bitmap(), m_used(), m_peak(), m_count(), m_address(Null<KVirtualAddress>), m_aligned_address(Null<KVirtualAddress>), m_size() { /* ... */ }
|
||||
|
||||
Result Initialize(KVirtualAddress memory, size_t sz) {
|
||||
Result Initialize(KVirtualAddress memory, size_t size, size_t align) {
|
||||
/* We need to have positive size. */
|
||||
R_UNLESS(sz > 0, svc::ResultOutOfMemory());
|
||||
R_UNLESS(size > 0, svc::ResultOutOfMemory());
|
||||
|
||||
/* Calculate management overhead. */
|
||||
const size_t management_size = KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer));
|
||||
const size_t allocatable_size = sz - management_size;
|
||||
/* Set addresses. */
|
||||
m_address = memory;
|
||||
m_aligned_address = util::AlignDown(GetInteger(memory), align);
|
||||
|
||||
/* Calculate extents. */
|
||||
const size_t managed_size = m_address + size - m_aligned_address;
|
||||
const size_t overhead_size = util::AlignUp(KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)), sizeof(PageBuffer));
|
||||
R_UNLESS(overhead_size < size, svc::ResultOutOfMemory());
|
||||
|
||||
/* Set tracking fields. */
|
||||
m_address = memory;
|
||||
m_size = util::AlignDown(allocatable_size, sizeof(PageBuffer));
|
||||
m_count = allocatable_size / sizeof(PageBuffer);
|
||||
R_UNLESS(m_count > 0, svc::ResultOutOfMemory());
|
||||
m_size = util::AlignDown(size - overhead_size, sizeof(PageBuffer));
|
||||
m_count = m_size / sizeof(PageBuffer);
|
||||
|
||||
/* Clear the management region. */
|
||||
u64 *management_ptr = GetPointer<u64>(m_address + allocatable_size);
|
||||
std::memset(management_ptr, 0, management_size);
|
||||
u64 *management_ptr = GetPointer<u64>(m_address + size - overhead_size);
|
||||
std::memset(management_ptr, 0, overhead_size);
|
||||
|
||||
/* Initialize the bitmap. */
|
||||
m_page_bitmap.Initialize(management_ptr, m_count);
|
||||
const size_t allocatable_region_size = (GetInteger(m_address) + size - overhead_size) - GetInteger(m_aligned_address);
|
||||
MESOSPHERE_ABORT_UNLESS(allocatable_region_size >= sizeof(PageBuffer));
|
||||
|
||||
m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer));
|
||||
|
||||
/* Free the pages to the bitmap. */
|
||||
for (size_t i = 0; i < m_count; i++) {
|
||||
@@ -69,7 +76,7 @@ namespace ams::kern {
|
||||
cpu::ClearPageToZero(GetPointer<PageBuffer>(m_address) + i);
|
||||
|
||||
/* Set the bit for the free page. */
|
||||
m_page_bitmap.SetBit(i);
|
||||
m_page_bitmap.SetBit((GetInteger(m_address) + (i * sizeof(PageBuffer)) - GetInteger(m_aligned_address)) / sizeof(PageBuffer));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -98,7 +105,28 @@ namespace ams::kern {
|
||||
m_page_bitmap.ClearBit(offset);
|
||||
m_peak = std::max(m_peak, (++m_used));
|
||||
|
||||
return GetPointer<PageBuffer>(m_address) + offset;
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
PageBuffer *Allocate(size_t count) {
|
||||
/* Take the lock. */
|
||||
KScopedInterruptDisable di;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
/* Find a random free block. */
|
||||
ssize_t soffset = m_page_bitmap.FindFreeRange(count);
|
||||
if (AMS_UNLIKELY(soffset < 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t offset = static_cast<size_t>(soffset);
|
||||
|
||||
/* Update our tracking. */
|
||||
m_page_bitmap.ClearRange(offset, count);
|
||||
m_used += count;
|
||||
m_peak = std::max(m_peak, m_used);
|
||||
|
||||
return GetPointer<PageBuffer>(m_aligned_address) + offset;
|
||||
}
|
||||
|
||||
void Free(PageBuffer *pb) {
|
||||
@@ -110,7 +138,7 @@ namespace ams::kern {
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
/* Set the bit for the free page. */
|
||||
size_t offset = (reinterpret_cast<uintptr_t>(pb) - GetInteger(m_address)) / sizeof(PageBuffer);
|
||||
size_t offset = (reinterpret_cast<uintptr_t>(pb) - GetInteger(m_aligned_address)) / sizeof(PageBuffer);
|
||||
m_page_bitmap.SetBit(offset);
|
||||
|
||||
/* Decrement our used count. */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -70,15 +70,20 @@ namespace ams::kern {
|
||||
u16 m_next_linear_id;
|
||||
u16 m_count;
|
||||
public:
|
||||
constexpr explicit KHandleTable(util::ConstantInitializeTag) : m_entry_infos(), m_objects(), m_lock(), m_free_head_index(-1), m_table_size(), m_max_count(), m_next_linear_id(MinLinearId), m_count() { /* ... */ }
|
||||
constexpr explicit KHandleTable(util::ConstantInitializeTag) : m_entry_infos(), m_objects(), m_lock(), m_free_head_index(-1), m_table_size(), m_max_count(), m_next_linear_id(), m_count() { /* ... */ }
|
||||
|
||||
explicit KHandleTable() : m_lock(), m_free_head_index(-1), m_count() { MESOSPHERE_ASSERT_THIS(); }
|
||||
explicit KHandleTable() : m_lock(), m_free_head_index(-1), m_table_size(), m_max_count(), m_next_linear_id(), m_count() { MESOSPHERE_ASSERT_THIS(); }
|
||||
|
||||
constexpr MESOSPHERE_NOINLINE_IF_DEBUG Result Initialize(s32 size) {
|
||||
MESOSPHERE_NOINLINE_IF_DEBUG Result Initialize(s32 size) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Check that the table size is valid. */
|
||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), svc::ResultOutOfMemory());
|
||||
|
||||
/* Lock. */
|
||||
KScopedDisableDispatch dd;
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
/* Initialize all fields. */
|
||||
m_max_count = 0;
|
||||
m_table_size = (size <= 0) ? MaxTableSize : size;
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KInterruptEventTask;
|
||||
|
||||
class KInterruptEvent final : public KAutoObjectWithSlabHeapAndContainer<KInterruptEvent, KReadableEvent> {
|
||||
class KInterruptEvent final : public KAutoObjectWithSlabHeapAndContainer<KInterruptEvent, KReadableEvent>, public KInterruptTask {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent);
|
||||
private:
|
||||
s32 m_interrupt_id;
|
||||
@@ -54,21 +52,9 @@ namespace ams::kern {
|
||||
static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ }
|
||||
|
||||
constexpr s32 GetInterruptId() const { return m_interrupt_id; }
|
||||
};
|
||||
|
||||
class KInterruptEventTask : public KSlabAllocated<KInterruptEventTask>, public KInterruptTask {
|
||||
private:
|
||||
KInterruptEvent *m_event;
|
||||
public:
|
||||
constexpr KInterruptEventTask() : m_event(nullptr) { /* ... */ }
|
||||
~KInterruptEventTask() { /* ... */ }
|
||||
|
||||
virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override;
|
||||
virtual void DoTask() override;
|
||||
|
||||
void Unregister(s32 interrupt_id, s32 core_id);
|
||||
public:
|
||||
static Result Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace ams::kern {
|
||||
KMemoryState_FlagCanMapProcess = (1 << 23),
|
||||
KMemoryState_FlagCanChangeAttribute = (1 << 24),
|
||||
KMemoryState_FlagCanCodeMemory = (1 << 25),
|
||||
KMemoryState_FlagLinearMapped = (1 << 26),
|
||||
|
||||
KMemoryState_FlagsData = KMemoryState_FlagCanReprotect | KMemoryState_FlagCanUseIpc |
|
||||
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
|
||||
@@ -50,25 +51,27 @@ namespace ams::kern {
|
||||
KMemoryState_FlagCanTransfer | KMemoryState_FlagCanQueryPhysical |
|
||||
KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap |
|
||||
KMemoryState_FlagCanIpcUserBuffer | KMemoryState_FlagReferenceCounted |
|
||||
KMemoryState_FlagCanChangeAttribute,
|
||||
KMemoryState_FlagCanChangeAttribute | KMemoryState_FlagLinearMapped,
|
||||
|
||||
KMemoryState_FlagsCode = KMemoryState_FlagCanDebug | KMemoryState_FlagCanUseIpc |
|
||||
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
|
||||
KMemoryState_FlagMapped | KMemoryState_FlagCode |
|
||||
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap |
|
||||
KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagReferenceCounted,
|
||||
KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagReferenceCounted |
|
||||
KMemoryState_FlagLinearMapped,
|
||||
|
||||
KMemoryState_FlagsMisc = KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted |
|
||||
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap,
|
||||
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap |
|
||||
KMemoryState_FlagLinearMapped,
|
||||
|
||||
|
||||
KMemoryState_Free = ams::svc::MemoryState_Free,
|
||||
KMemoryState_Io = ams::svc::MemoryState_Io | KMemoryState_FlagMapped,
|
||||
KMemoryState_Io = ams::svc::MemoryState_Io | KMemoryState_FlagMapped | KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap,
|
||||
KMemoryState_Static = ams::svc::MemoryState_Static | KMemoryState_FlagMapped | KMemoryState_FlagCanQueryPhysical,
|
||||
KMemoryState_Code = ams::svc::MemoryState_Code | KMemoryState_FlagsCode | KMemoryState_FlagCanMapProcess,
|
||||
KMemoryState_CodeData = ams::svc::MemoryState_CodeData | KMemoryState_FlagsData | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeMemory,
|
||||
KMemoryState_Normal = ams::svc::MemoryState_Normal | KMemoryState_FlagsData | KMemoryState_FlagCanCodeMemory,
|
||||
KMemoryState_Shared = ams::svc::MemoryState_Shared | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
|
||||
KMemoryState_Shared = ams::svc::MemoryState_Shared | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped,
|
||||
|
||||
/* KMemoryState_Alias was removed after 1.0.0. */
|
||||
|
||||
@@ -82,7 +85,7 @@ namespace ams::kern {
|
||||
KMemoryState_Stack = ams::svc::MemoryState_Stack | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
|
||||
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
|
||||
|
||||
KMemoryState_ThreadLocal = ams::svc::MemoryState_ThreadLocal | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
|
||||
KMemoryState_ThreadLocal = ams::svc::MemoryState_ThreadLocal | KMemoryState_FlagMapped | KMemoryState_FlagLinearMapped,
|
||||
|
||||
KMemoryState_Transfered = ams::svc::MemoryState_Transfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanChangeAttribute
|
||||
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
|
||||
@@ -90,7 +93,7 @@ namespace ams::kern {
|
||||
KMemoryState_SharedTransfered = ams::svc::MemoryState_SharedTransfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
|
||||
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
|
||||
|
||||
KMemoryState_SharedCode = ams::svc::MemoryState_SharedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted
|
||||
KMemoryState_SharedCode = ams::svc::MemoryState_SharedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped
|
||||
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
|
||||
|
||||
KMemoryState_Inaccessible = ams::svc::MemoryState_Inaccessible,
|
||||
@@ -103,36 +106,41 @@ namespace ams::kern {
|
||||
|
||||
KMemoryState_Kernel = ams::svc::MemoryState_Kernel | KMemoryState_FlagMapped,
|
||||
|
||||
KMemoryState_GeneratedCode = ams::svc::MemoryState_GeneratedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDebug,
|
||||
KMemoryState_CodeOut = ams::svc::MemoryState_CodeOut | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
|
||||
KMemoryState_GeneratedCode = ams::svc::MemoryState_GeneratedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDebug | KMemoryState_FlagLinearMapped,
|
||||
KMemoryState_CodeOut = ams::svc::MemoryState_CodeOut | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped,
|
||||
|
||||
KMemoryState_Coverage = ams::svc::MemoryState_Coverage | KMemoryState_FlagMapped,
|
||||
|
||||
KMemoryState_Insecure = ams::svc::MemoryState_Insecure | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped | KMemoryState_FlagCanChangeAttribute
|
||||
| KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap
|
||||
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
|
||||
};
|
||||
|
||||
#if 1
|
||||
static_assert(KMemoryState_Free == 0x00000000);
|
||||
static_assert(KMemoryState_Io == 0x00002001);
|
||||
static_assert(KMemoryState_Io == 0x00182001);
|
||||
static_assert(KMemoryState_Static == 0x00042002);
|
||||
static_assert(KMemoryState_Code == 0x00DC7E03);
|
||||
static_assert(KMemoryState_CodeData == 0x03FEBD04);
|
||||
static_assert(KMemoryState_Normal == 0x037EBD05);
|
||||
static_assert(KMemoryState_Shared == 0x00402006);
|
||||
static_assert(KMemoryState_Code == 0x04DC7E03);
|
||||
static_assert(KMemoryState_CodeData == 0x07FEBD04);
|
||||
static_assert(KMemoryState_Normal == 0x077EBD05);
|
||||
static_assert(KMemoryState_Shared == 0x04402006);
|
||||
|
||||
static_assert(KMemoryState_AliasCode == 0x00DD7E08);
|
||||
static_assert(KMemoryState_AliasCodeData == 0x03FFBD09);
|
||||
static_assert(KMemoryState_Ipc == 0x005C3C0A);
|
||||
static_assert(KMemoryState_Stack == 0x005C3C0B);
|
||||
static_assert(KMemoryState_ThreadLocal == 0x0040200C);
|
||||
static_assert(KMemoryState_Transfered == 0x015C3C0D);
|
||||
static_assert(KMemoryState_SharedTransfered == 0x005C380E);
|
||||
static_assert(KMemoryState_SharedCode == 0x0040380F);
|
||||
static_assert(KMemoryState_AliasCode == 0x04DD7E08);
|
||||
static_assert(KMemoryState_AliasCodeData == 0x07FFBD09);
|
||||
static_assert(KMemoryState_Ipc == 0x045C3C0A);
|
||||
static_assert(KMemoryState_Stack == 0x045C3C0B);
|
||||
static_assert(KMemoryState_ThreadLocal == 0x0400200C);
|
||||
static_assert(KMemoryState_Transfered == 0x055C3C0D);
|
||||
static_assert(KMemoryState_SharedTransfered == 0x045C380E);
|
||||
static_assert(KMemoryState_SharedCode == 0x0440380F);
|
||||
static_assert(KMemoryState_Inaccessible == 0x00000010);
|
||||
static_assert(KMemoryState_NonSecureIpc == 0x005C3811);
|
||||
static_assert(KMemoryState_NonDeviceIpc == 0x004C2812);
|
||||
static_assert(KMemoryState_NonSecureIpc == 0x045C3811);
|
||||
static_assert(KMemoryState_NonDeviceIpc == 0x044C2812);
|
||||
static_assert(KMemoryState_Kernel == 0x00002013);
|
||||
static_assert(KMemoryState_GeneratedCode == 0x00402214);
|
||||
static_assert(KMemoryState_CodeOut == 0x00402015);
|
||||
static_assert(KMemoryState_GeneratedCode == 0x04402214);
|
||||
static_assert(KMemoryState_CodeOut == 0x04402015);
|
||||
static_assert(KMemoryState_Coverage == 0x00002016);
|
||||
static_assert(KMemoryState_Insecure == 0x05583817);
|
||||
#endif
|
||||
|
||||
enum KMemoryPermission : u8 {
|
||||
|
||||
@@ -50,9 +50,11 @@ namespace ams::kern {
|
||||
constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
|
||||
|
||||
/* NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x800. */
|
||||
constexpr size_t KernelPageBufferHeapSize = 0x3E0000;
|
||||
constexpr size_t KernelSlabHeapAdditionalSize = 0x148000;
|
||||
constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
|
||||
|
||||
constexpr size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
|
||||
constexpr size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize + KernelPageBufferHeapSize;
|
||||
|
||||
class KMemoryLayout {
|
||||
private:
|
||||
@@ -150,6 +152,8 @@ namespace ams::kern {
|
||||
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG const KMemoryRegion &GetKernelTraceBufferRegion() { return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelTraceBuffer)); }
|
||||
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG const KMemoryRegion &GetSecureAppletMemoryRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelSecureAppletMemory)); }
|
||||
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG const KMemoryRegion &GetVirtualLinearRegion(KVirtualAddress address) { return Dereference(FindLinear(address)); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG const KMemoryRegion &GetPhysicalLinearRegion(KPhysicalAddress address) { return Dereference(FindLinear(address)); }
|
||||
|
||||
@@ -179,15 +183,15 @@ 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();
|
||||
|
||||
static size_t GetResourceRegionSizeForInit();
|
||||
static size_t GetResourceRegionSizeForInit(bool use_extra_resource);
|
||||
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelCodeRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode); }
|
||||
@@ -209,6 +213,7 @@ namespace ams::kern {
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelBase); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelCodeRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelCode); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelSlabRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelSlab); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelSecureAppletMemoryRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelSecureAppletMemory); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelPageTableHeapRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); }
|
||||
static MESOSPHERE_NOINLINE_IF_DEBUG auto GetKernelInitPageTableRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); }
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace ams::kern {
|
||||
|
||||
NOINLINE KPhysicalAddress AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
NOINLINE Result AllocateAndOpen(KPageGroup *out, size_t num_pages, u32 option);
|
||||
NOINLINE Result AllocateAndOpenForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern);
|
||||
NOINLINE Result AllocateForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern);
|
||||
|
||||
Pool GetPool(KPhysicalAddress address) const {
|
||||
return this->GetManager(address).GetPool();
|
||||
@@ -222,6 +222,22 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenFirst(KPhysicalAddress address, size_t num_pages) {
|
||||
/* Repeatedly open references until we've done so for all pages. */
|
||||
while (num_pages) {
|
||||
auto &manager = this->GetManager(address);
|
||||
const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
|
||||
|
||||
{
|
||||
KScopedLightLock lk(m_pool_locks[manager.GetPool()]);
|
||||
manager.OpenFirst(address, cur_pages);
|
||||
}
|
||||
|
||||
num_pages -= cur_pages;
|
||||
address += cur_pages * PageSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Close(KPhysicalAddress address, size_t num_pages) {
|
||||
/* Repeatedly close references until we've done so for all pages. */
|
||||
while (num_pages) {
|
||||
|
||||
@@ -211,6 +211,8 @@ namespace ams::kern {
|
||||
static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() == (0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped));
|
||||
static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() == (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr inline const auto KMemoryRegionType_DramKernelSecureAppletMemory = KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() == (0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr inline const auto KMemoryRegionType_DramReservedEarly = KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() == (0x16 | KMemoryRegionAttr_NoUserMap));
|
||||
@@ -251,6 +253,9 @@ namespace ams::kern {
|
||||
constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug = KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
||||
|
||||
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory = KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
|
||||
|
||||
constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
||||
constexpr inline const auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
||||
@@ -327,6 +332,8 @@ namespace ams::kern {
|
||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||
} else {
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace ams::kern {
|
||||
/* Check that the object is closed. */
|
||||
R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState());
|
||||
|
||||
return Delete(obj.GetPointerUnsafe(), name);
|
||||
R_RETURN(Delete(obj.GetPointerUnsafe(), name));
|
||||
}
|
||||
|
||||
template<typename Derived> requires std::derived_from<Derived, KAutoObject>
|
||||
|
||||
@@ -113,11 +113,12 @@ namespace ams::kern {
|
||||
static constexpr size_t MaxDepth = 4;
|
||||
private:
|
||||
u64 *m_bit_storages[MaxDepth];
|
||||
u64 *m_end_storages[MaxDepth];
|
||||
RandomBitGenerator m_rng;
|
||||
size_t m_num_bits;
|
||||
size_t m_used_depths;
|
||||
public:
|
||||
KPageBitmap() : m_bit_storages(), m_rng(), m_num_bits(), m_used_depths() { /* ... */ }
|
||||
KPageBitmap() : m_bit_storages(), m_end_storages(), m_rng(), m_num_bits(), m_used_depths() { /* ... */ }
|
||||
|
||||
constexpr size_t GetNumBits() const { return m_num_bits; }
|
||||
constexpr s32 GetHighestDepthIndex() const { return static_cast<s32>(m_used_depths) - 1; }
|
||||
@@ -135,6 +136,7 @@ namespace ams::kern {
|
||||
m_bit_storages[depth] = storage;
|
||||
size = util::AlignUp(size, BITSIZEOF(u64)) / BITSIZEOF(u64);
|
||||
storage += size;
|
||||
m_end_storages[depth] = storage;
|
||||
}
|
||||
|
||||
return storage;
|
||||
@@ -171,6 +173,45 @@ namespace ams::kern {
|
||||
return static_cast<ssize_t>(offset);
|
||||
}
|
||||
|
||||
ssize_t FindFreeRange(size_t count) {
|
||||
/* Check that it is possible to find a range. */
|
||||
const u64 * const storage_start = m_bit_storages[m_used_depths - 1];
|
||||
const u64 * const storage_end = m_end_storages[m_used_depths - 1];
|
||||
|
||||
/* If we don't have a storage to iterate (or want more blocks than fit in a single storage), we can't find a free range. */
|
||||
if (!(storage_start < storage_end && count <= BITSIZEOF(u64))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Walk the storages to select a random free range. */
|
||||
const size_t options_per_storage = std::max<size_t>(BITSIZEOF(u64) / count, 1);
|
||||
const size_t num_entries = std::max<size_t>(storage_end - storage_start, 1);
|
||||
|
||||
const u64 free_mask = (static_cast<u64>(1) << count) - 1;
|
||||
|
||||
size_t num_valid_options = 0;
|
||||
ssize_t chosen_offset = -1;
|
||||
for (size_t storage_index = 0; storage_index < num_entries; ++storage_index) {
|
||||
u64 storage = storage_start[storage_index];
|
||||
for (size_t option = 0; option < options_per_storage; ++option) {
|
||||
if ((storage & free_mask) == free_mask) {
|
||||
/* We've found a new valid option. */
|
||||
++num_valid_options;
|
||||
|
||||
/* Select the Kth valid option with probability 1/K. This leads to an overall uniform distribution. */
|
||||
if (num_valid_options == 1 || m_rng.GenerateRandom(num_valid_options) == 0) {
|
||||
/* This is our first option, so select it. */
|
||||
chosen_offset = storage_index * BITSIZEOF(u64) + option * count;
|
||||
}
|
||||
}
|
||||
storage >>= count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the random offset we chose.*/
|
||||
return chosen_offset;
|
||||
}
|
||||
|
||||
void SetBit(size_t offset) {
|
||||
this->SetBit(this->GetHighestDepthIndex(), offset);
|
||||
m_num_bits++;
|
||||
|
||||
@@ -16,12 +16,33 @@
|
||||
#pragma once
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_memory_layout.hpp>
|
||||
#include <mesosphere/kern_k_dynamic_page_manager.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KPageBuffer : public KSlabAllocated<KPageBuffer> {
|
||||
class KDynamicPageManager;
|
||||
|
||||
class KPageBuffer;
|
||||
|
||||
class KPageBufferSlabHeap : protected impl::KSlabHeapImpl {
|
||||
public:
|
||||
static constexpr size_t BufferSize = PageSize;
|
||||
static constinit inline size_t s_buffer_count = 0;
|
||||
private:
|
||||
alignas(PageSize) u8 m_buffer[PageSize];
|
||||
size_t m_obj_size{};
|
||||
public:
|
||||
constexpr KPageBufferSlabHeap() = default;
|
||||
|
||||
/* See kern_init_slab_setup.cpp for definition. */
|
||||
void Initialize(KDynamicPageManager &allocator);
|
||||
|
||||
KPageBuffer *Allocate();
|
||||
void Free(KPageBuffer *pb);
|
||||
};
|
||||
|
||||
class KPageBuffer {
|
||||
private:
|
||||
u8 m_buffer[KPageBufferSlabHeap::BufferSize];
|
||||
public:
|
||||
KPageBuffer() {
|
||||
std::memset(m_buffer, 0, sizeof(m_buffer));
|
||||
@@ -39,8 +60,49 @@ namespace ams::kern {
|
||||
|
||||
return GetPointer<KPageBuffer>(virt_addr);
|
||||
}
|
||||
private:
|
||||
static constinit inline KPageBufferSlabHeap s_slab_heap;
|
||||
public:
|
||||
static void InitializeSlabHeap(KDynamicPageManager &allocator) {
|
||||
s_slab_heap.Initialize(allocator);
|
||||
}
|
||||
|
||||
static KPageBuffer *Allocate() {
|
||||
return s_slab_heap.Allocate();
|
||||
}
|
||||
|
||||
static void Free(KPageBuffer *obj) {
|
||||
s_slab_heap.Free(obj);
|
||||
}
|
||||
|
||||
template<size_t ExpectedSize>
|
||||
static ALWAYS_INLINE KPageBuffer *AllocateChecked() {
|
||||
/* Check that the allocation is valid. */
|
||||
MESOSPHERE_ABORT_UNLESS(sizeof(KPageBuffer) == ExpectedSize);
|
||||
|
||||
return Allocate();
|
||||
}
|
||||
|
||||
template<size_t ExpectedSize>
|
||||
static ALWAYS_INLINE void FreeChecked(KPageBuffer *obj) {
|
||||
/* Check that the free is valid. */
|
||||
MESOSPHERE_ABORT_UNLESS(sizeof(KPageBuffer) == ExpectedSize);
|
||||
|
||||
return Free(obj);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(KPageBuffer) == PageSize);
|
||||
static_assert(alignof(KPageBuffer) == PageSize);
|
||||
static_assert(sizeof(KPageBuffer) == KPageBufferSlabHeap::BufferSize);
|
||||
|
||||
ALWAYS_INLINE KPageBuffer *KPageBufferSlabHeap::Allocate() {
|
||||
KPageBuffer *pb = static_cast<KPageBuffer *>(KSlabHeapImpl::Allocate());
|
||||
if (AMS_LIKELY(pb != nullptr)) {
|
||||
std::construct_at(pb);
|
||||
}
|
||||
return pb;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void KPageBufferSlabHeap::Free(KPageBuffer *pb) {
|
||||
KSlabHeapImpl::Free(pb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ namespace ams::kern {
|
||||
|
||||
Result AddBlock(KPhysicalAddress addr, size_t num_pages);
|
||||
void Open() const;
|
||||
void OpenFirst() const;
|
||||
void Close() const;
|
||||
|
||||
size_t GetNumPages() const;
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace ams::kern {
|
||||
static_assert(sizeof(KPageProperties) == sizeof(u32));
|
||||
|
||||
class KResourceLimit;
|
||||
class KSystemResource;
|
||||
|
||||
class KPageTableBase {
|
||||
NON_COPYABLE(KPageTableBase);
|
||||
@@ -71,11 +72,14 @@ namespace ams::kern {
|
||||
};
|
||||
|
||||
enum OperationType {
|
||||
OperationType_Map = 0,
|
||||
OperationType_MapGroup = 1,
|
||||
OperationType_Unmap = 2,
|
||||
OperationType_ChangePermissions = 3,
|
||||
OperationType_ChangePermissionsAndRefresh = 4,
|
||||
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;
|
||||
@@ -162,6 +166,7 @@ namespace ams::kern {
|
||||
size_t m_max_heap_size;
|
||||
size_t m_mapped_physical_memory_size;
|
||||
size_t m_mapped_unsafe_physical_memory;
|
||||
size_t m_mapped_insecure_memory;
|
||||
size_t m_mapped_ipc_server_memory;
|
||||
mutable KLightLock m_general_lock;
|
||||
mutable KLightLock m_map_physical_memory_lock;
|
||||
@@ -188,8 +193,8 @@ namespace ams::kern {
|
||||
m_alias_region_end(Null<KProcessAddress>), m_stack_region_start(Null<KProcessAddress>), m_stack_region_end(Null<KProcessAddress>),
|
||||
m_kernel_map_region_start(Null<KProcessAddress>), m_kernel_map_region_end(Null<KProcessAddress>), m_alias_code_region_start(Null<KProcessAddress>),
|
||||
m_alias_code_region_end(Null<KProcessAddress>), m_code_region_start(Null<KProcessAddress>), m_code_region_end(Null<KProcessAddress>),
|
||||
m_max_heap_size(), m_mapped_physical_memory_size(), m_mapped_unsafe_physical_memory(), m_mapped_ipc_server_memory(), m_general_lock(),
|
||||
m_map_physical_memory_lock(), m_device_map_lock(), m_impl(util::ConstantInitialize), m_memory_block_manager(util::ConstantInitialize),
|
||||
m_max_heap_size(), m_mapped_physical_memory_size(), m_mapped_unsafe_physical_memory(), m_mapped_insecure_memory(), m_mapped_ipc_server_memory(),
|
||||
m_general_lock(), m_map_physical_memory_lock(), m_device_map_lock(), m_impl(util::ConstantInitialize), m_memory_block_manager(util::ConstantInitialize),
|
||||
m_allocate_option(), m_address_space_width(), m_is_kernel(), m_enable_aslr(), m_enable_device_address_space_merge(),
|
||||
m_memory_block_slab_manager(), m_block_info_manager(), m_resource_limit(), m_cached_physical_linear_region(), m_cached_physical_heap_region(),
|
||||
m_heap_fill_value(), m_ipc_fill_value(), m_stack_fill_value()
|
||||
@@ -200,7 +205,7 @@ namespace ams::kern {
|
||||
explicit KPageTableBase() { /* ... */ }
|
||||
|
||||
NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end);
|
||||
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KResourceLimit *resource_limit);
|
||||
NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_device_address_space_merge, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit);
|
||||
|
||||
void Finalize();
|
||||
|
||||
@@ -359,9 +364,11 @@ 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);
|
||||
Result UnmapInsecureMemory(KProcessAddress address, size_t size);
|
||||
|
||||
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, region_num_pages, state, perm));
|
||||
@@ -392,8 +399,8 @@ namespace ams::kern {
|
||||
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size);
|
||||
Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size);
|
||||
|
||||
Result LockForMapDeviceAddressSpace(KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
|
||||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size);
|
||||
Result LockForMapDeviceAddressSpace(bool *out_is_io, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned, bool check_heap);
|
||||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap);
|
||||
|
||||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
|
||||
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size);
|
||||
|
||||
@@ -234,11 +234,11 @@ namespace ams::kern {
|
||||
KPriorityQueueImpl m_scheduled_queue;
|
||||
KPriorityQueueImpl m_suggested_queue;
|
||||
private:
|
||||
constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
|
||||
static constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
|
||||
affinity &= ~(UINT64_C(1) << core);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
|
||||
static constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
|
||||
const s32 core = __builtin_ctzll(static_cast<unsigned long long>(affinity));
|
||||
ClearAffinityBit(affinity, core);
|
||||
return core;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <mesosphere/kern_k_wait_object.hpp>
|
||||
#include <mesosphere/kern_k_dynamic_resource_manager.hpp>
|
||||
#include <mesosphere/kern_k_page_table_manager.hpp>
|
||||
#include <mesosphere/kern_k_system_resource.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
@@ -64,8 +65,7 @@ namespace ams::kern {
|
||||
s32 m_ideal_core_id;
|
||||
void *m_attached_object;
|
||||
KResourceLimit *m_resource_limit;
|
||||
KVirtualAddress m_system_resource_address;
|
||||
size_t m_system_resource_num_pages;
|
||||
KSystemResource *m_system_resource;
|
||||
size_t m_memory_release_hint;
|
||||
State m_state;
|
||||
KLightLock m_state_lock;
|
||||
@@ -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;
|
||||
@@ -117,13 +120,6 @@ namespace ams::kern {
|
||||
util::Atomic<s64> m_num_ipc_messages;
|
||||
util::Atomic<s64> m_num_ipc_replies;
|
||||
util::Atomic<s64> m_num_ipc_receives;
|
||||
KDynamicPageManager m_dynamic_page_manager;
|
||||
KMemoryBlockSlabManager m_memory_block_slab_manager;
|
||||
KBlockInfoManager m_block_info_manager;
|
||||
KPageTableManager m_page_table_manager;
|
||||
KMemoryBlockSlabHeap m_memory_block_heap;
|
||||
KBlockInfoSlabHeap m_block_info_heap;
|
||||
KPageTableSlabHeap m_page_table_heap;
|
||||
private:
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms);
|
||||
|
||||
@@ -284,17 +280,18 @@ namespace ams::kern {
|
||||
void IncrementRunningThreadCount();
|
||||
void DecrementRunningThreadCount();
|
||||
|
||||
size_t GetTotalSystemResourceSize() const { return m_system_resource_num_pages * PageSize; }
|
||||
size_t GetUsedSystemResourceSize() const {
|
||||
if (m_system_resource_num_pages == 0) {
|
||||
return 0;
|
||||
}
|
||||
return m_dynamic_page_manager.GetUsed() * PageSize;
|
||||
size_t GetTotalSystemResourceSize() const {
|
||||
return m_system_resource->IsSecureResource() ? static_cast<KSecureSystemResource *>(m_system_resource)->GetSize() : 0;
|
||||
}
|
||||
|
||||
void SetRunningThread(s32 core, KThread *thread, u64 idle_count) {
|
||||
m_running_threads[core] = thread;
|
||||
m_running_thread_idle_counts[core] = idle_count;
|
||||
size_t GetUsedSystemResourceSize() const {
|
||||
return m_system_resource->IsSecureResource() ? static_cast<KSecureSystemResource *>(m_system_resource)->GetUsedSize() : 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -305,13 +302,15 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
const KDynamicPageManager &GetDynamicPageManager() const { return m_dynamic_page_manager; }
|
||||
const KMemoryBlockSlabManager &GetMemoryBlockSlabManager() const { return m_memory_block_slab_manager; }
|
||||
const KBlockInfoManager &GetBlockInfoManager() const { return m_block_info_manager; }
|
||||
const KPageTableManager &GetPageTableManager() const { return m_page_table_manager; }
|
||||
const KSystemResource &GetSystemResource() const { return *m_system_resource; }
|
||||
|
||||
const KMemoryBlockSlabManager &GetMemoryBlockSlabManager() const { return m_system_resource->GetMemoryBlockSlabManager(); }
|
||||
const KBlockInfoManager &GetBlockInfoManager() const { return m_system_resource->GetBlockInfoManager(); }
|
||||
const KPageTableManager &GetPageTableManager() const { return m_system_resource->GetPageTableManager(); }
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -152,7 +158,7 @@ namespace ams::kern {
|
||||
static NOINLINE void OnThreadPriorityChanged(KThread *thread, s32 old_priority);
|
||||
static NOINLINE void OnThreadAffinityMaskChanged(KThread *thread, const KAffinityMask &old_affinity, s32 old_core);
|
||||
|
||||
static NOINLINE void RotateScheduledQueue(s32 priority, s32 core_id);
|
||||
static NOINLINE void RotateScheduledQueue(s32 core_id, s32 priority);
|
||||
|
||||
static NOINLINE void YieldWithoutCoreMigration();
|
||||
static NOINLINE void YieldWithCoreMigration();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -29,7 +29,10 @@ namespace ams::kern {
|
||||
public:
|
||||
class SessionMappings {
|
||||
private:
|
||||
static constexpr size_t NumStaticMappings = 8;
|
||||
/* At most 15 buffers of each type (4-bit descriptor counts), for 45 total. */
|
||||
static constexpr size_t NumMappings = ((1ul << 4) - 1) * 3;
|
||||
static constexpr size_t NumStaticMappings = 8;
|
||||
static constexpr size_t NumDynamicMappings = NumMappings - NumStaticMappings;
|
||||
|
||||
class Mapping {
|
||||
private:
|
||||
@@ -50,16 +53,27 @@ namespace ams::kern {
|
||||
constexpr ALWAYS_INLINE size_t GetSize() const { return m_size; }
|
||||
constexpr ALWAYS_INLINE KMemoryState GetMemoryState() const { return m_state; }
|
||||
};
|
||||
public:
|
||||
class DynamicMappings : public KSlabAllocated<DynamicMappings, true> {
|
||||
private:
|
||||
Mapping m_mappings[NumDynamicMappings];
|
||||
public:
|
||||
constexpr explicit DynamicMappings() : m_mappings() { /* ... */ }
|
||||
|
||||
constexpr ALWAYS_INLINE Mapping &Get(size_t idx) { return m_mappings[idx]; }
|
||||
constexpr ALWAYS_INLINE const Mapping &Get(size_t idx) const { return m_mappings[idx]; }
|
||||
};
|
||||
static_assert(sizeof(DynamicMappings) == sizeof(Mapping) * NumDynamicMappings);
|
||||
private:
|
||||
Mapping m_static_mappings[NumStaticMappings];
|
||||
Mapping *m_mappings;
|
||||
DynamicMappings *m_dynamic_mappings;
|
||||
u8 m_num_send;
|
||||
u8 m_num_recv;
|
||||
u8 m_num_exch;
|
||||
public:
|
||||
constexpr explicit SessionMappings(util::ConstantInitializeTag) : m_static_mappings(), m_mappings(), m_num_send(), m_num_recv(), m_num_exch() { /* ... */ }
|
||||
constexpr explicit SessionMappings(util::ConstantInitializeTag) : m_static_mappings(), m_dynamic_mappings(), m_num_send(), m_num_recv(), m_num_exch() { /* ... */ }
|
||||
|
||||
explicit SessionMappings() : m_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() { /* ... */ }
|
||||
explicit SessionMappings() : m_dynamic_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() { /* ... */ }
|
||||
|
||||
void Initialize() { /* ... */ }
|
||||
void Finalize();
|
||||
@@ -96,7 +110,7 @@ namespace ams::kern {
|
||||
if (index < NumStaticMappings) {
|
||||
return m_static_mappings[index];
|
||||
} else {
|
||||
return m_mappings[index - NumStaticMappings];
|
||||
return m_dynamic_mappings->Get(index - NumStaticMappings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +121,7 @@ namespace ams::kern {
|
||||
if (index < NumStaticMappings) {
|
||||
return m_static_mappings[index];
|
||||
} else {
|
||||
return m_mappings[index - NumStaticMappings];
|
||||
return m_dynamic_mappings->Get(index - NumStaticMappings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +132,7 @@ namespace ams::kern {
|
||||
if (index < NumStaticMappings) {
|
||||
return m_static_mappings[index];
|
||||
} else {
|
||||
return m_mappings[index - NumStaticMappings];
|
||||
return m_dynamic_mappings->Get(index - NumStaticMappings);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ams::kern {
|
||||
bool m_is_initialized;
|
||||
public:
|
||||
explicit KSharedMemory()
|
||||
: m_page_group(std::addressof(Kernel::GetSystemBlockInfoManager())), m_resource_limit(nullptr), m_owner_process_id(std::numeric_limits<u64>::max()),
|
||||
: m_page_group(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer()), m_resource_limit(nullptr), m_owner_process_id(std::numeric_limits<u64>::max()),
|
||||
m_owner_perm(ams::svc::MemoryPermission_None), m_remote_perm(ams::svc::MemoryPermission_None), m_is_initialized(false)
|
||||
{
|
||||
/* ... */
|
||||
|
||||
@@ -21,11 +21,22 @@ namespace ams::kern {
|
||||
|
||||
struct InitialProcessBinaryLayout;
|
||||
|
||||
namespace init {
|
||||
|
||||
struct KInitArguments;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KResourceLimit;
|
||||
|
||||
class KSystemControlBase {
|
||||
public:
|
||||
/* This can be overridden as needed. */
|
||||
static constexpr size_t SecureAppletMemorySize = 0;
|
||||
protected:
|
||||
/* Nintendo uses std::mt19937_t for randomness. */
|
||||
/* To save space (and because mt19337_t isn't secure anyway), */
|
||||
@@ -35,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();
|
||||
@@ -42,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();
|
||||
@@ -52,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();
|
||||
|
||||
@@ -83,6 +98,10 @@ namespace ams::kern {
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||
static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
|
||||
static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
|
||||
|
||||
/* Insecure Memory. */
|
||||
static KResourceLimit *GetInsecureMemoryResourceLimit();
|
||||
static u32 GetInsecureMemoryPool();
|
||||
protected:
|
||||
template<typename F>
|
||||
static ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_auto_object.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_memory_manager.hpp>
|
||||
#include <mesosphere/kern_k_dynamic_resource_manager.hpp>
|
||||
#include <mesosphere/kern_k_page_table_manager.hpp>
|
||||
#include <mesosphere/kern_k_resource_limit.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
/* NOTE: Nintendo's implementation does not have the "is_secure_resource" field, and instead uses virtual IsSecureResource(). */
|
||||
|
||||
class KSystemResource : public KAutoObject {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KSystemResource, KAutoObject);
|
||||
private:
|
||||
KMemoryBlockSlabManager *m_p_memory_block_slab_manager{};
|
||||
KBlockInfoManager *m_p_block_info_manager{};
|
||||
KPageTableManager *m_p_page_table_manager{};
|
||||
bool m_is_secure_resource{false};
|
||||
public:
|
||||
explicit KSystemResource() : KAutoObject() { /* ... */ }
|
||||
|
||||
constexpr explicit KSystemResource(util::ConstantInitializeTag) : KAutoObject(util::ConstantInitialize) { /* ... */ }
|
||||
protected:
|
||||
ALWAYS_INLINE void SetSecureResource() { m_is_secure_resource = true; }
|
||||
public:
|
||||
virtual void Destroy() override { MESOSPHERE_PANIC("KSystemResource::Destroy() was called"); }
|
||||
|
||||
ALWAYS_INLINE bool IsSecureResource() const { return m_is_secure_resource; }
|
||||
|
||||
void SetManagers(KMemoryBlockSlabManager &mb, KBlockInfoManager &bi, KPageTableManager &pt) {
|
||||
MESOSPHERE_ASSERT(m_p_memory_block_slab_manager == nullptr);
|
||||
MESOSPHERE_ASSERT(m_p_block_info_manager == nullptr);
|
||||
MESOSPHERE_ASSERT(m_p_page_table_manager == nullptr);
|
||||
|
||||
m_p_memory_block_slab_manager = std::addressof(mb);
|
||||
m_p_block_info_manager = std::addressof(bi);
|
||||
m_p_page_table_manager = std::addressof(pt);
|
||||
}
|
||||
|
||||
const KMemoryBlockSlabManager &GetMemoryBlockSlabManager() const { return *m_p_memory_block_slab_manager; }
|
||||
const KBlockInfoManager &GetBlockInfoManager() const { return *m_p_block_info_manager; }
|
||||
const KPageTableManager &GetPageTableManager() const { return *m_p_page_table_manager; }
|
||||
|
||||
KMemoryBlockSlabManager &GetMemoryBlockSlabManager() { return *m_p_memory_block_slab_manager; }
|
||||
KBlockInfoManager &GetBlockInfoManager() { return *m_p_block_info_manager; }
|
||||
KPageTableManager &GetPageTableManager() { return *m_p_page_table_manager; }
|
||||
|
||||
KMemoryBlockSlabManager *GetMemoryBlockSlabManagerPointer() { return m_p_memory_block_slab_manager; }
|
||||
KBlockInfoManager *GetBlockInfoManagerPointer() { return m_p_block_info_manager; }
|
||||
KPageTableManager *GetPageTableManagerPointer() { return m_p_page_table_manager; }
|
||||
};
|
||||
|
||||
class KSecureSystemResource final : public KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource> {
|
||||
private:
|
||||
bool m_is_initialized;
|
||||
KMemoryManager::Pool m_resource_pool;
|
||||
KDynamicPageManager m_dynamic_page_manager;
|
||||
KMemoryBlockSlabManager m_memory_block_slab_manager;
|
||||
KBlockInfoManager m_block_info_manager;
|
||||
KPageTableManager m_page_table_manager;
|
||||
KMemoryBlockSlabHeap m_memory_block_heap;
|
||||
KBlockInfoSlabHeap m_block_info_heap;
|
||||
KPageTableSlabHeap m_page_table_heap;
|
||||
KResourceLimit *m_resource_limit;
|
||||
KVirtualAddress m_resource_address;
|
||||
size_t m_resource_size;
|
||||
public:
|
||||
explicit KSecureSystemResource() : m_is_initialized(false), m_resource_limit(nullptr) {
|
||||
/* Mark ourselves as being a secure resource. */
|
||||
this->SetSecureResource();
|
||||
}
|
||||
|
||||
Result Initialize(size_t size, KResourceLimit *resource_limit, KMemoryManager::Pool pool);
|
||||
void Finalize();
|
||||
|
||||
bool IsInitialized() const { return m_is_initialized; }
|
||||
static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ }
|
||||
|
||||
ALWAYS_INLINE size_t CalculateRequiredSecureMemorySize() const {
|
||||
return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE size_t GetSize() const { return m_resource_size; }
|
||||
ALWAYS_INLINE size_t GetUsedSize() const { return m_dynamic_page_manager.GetUsed() * PageSize; }
|
||||
|
||||
const KDynamicPageManager &GetDynamicPageManager() const { return m_dynamic_page_manager; }
|
||||
public:
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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()) {
|
||||
/* Sort 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; }
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace ams::kern {
|
||||
static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
|
||||
static constexpr size_t SystemMemoryBlockSlabHeapSize = 10000;
|
||||
static constexpr size_t BlockInfoSlabHeapSize = 4000;
|
||||
static constexpr size_t ReservedDynamicPageCount = 70;
|
||||
static constexpr size_t ReservedDynamicPageCount = 64;
|
||||
private:
|
||||
static State s_state;
|
||||
static KResourceLimit s_system_resource_limit;
|
||||
@@ -78,6 +78,8 @@ namespace ams::kern {
|
||||
static KMemoryBlockSlabManager s_sys_memory_block_manager;
|
||||
static KBlockInfoManager s_app_block_info_manager;
|
||||
static KBlockInfoManager s_sys_block_info_manager;
|
||||
static KSystemResource s_app_system_resource;
|
||||
static KSystemResource s_sys_system_resource;
|
||||
static KSupervisorPageTable s_supervisor_page_table;
|
||||
static KUnsafeMemory s_unsafe_memory;
|
||||
static KWorkerTaskManager s_worker_task_managers[KWorkerTaskManager::WorkerType_Count];
|
||||
@@ -129,28 +131,12 @@ namespace ams::kern {
|
||||
return s_memory_manager;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KMemoryBlockSlabManager &GetApplicationMemoryBlockManager() {
|
||||
return s_app_memory_block_manager;
|
||||
static ALWAYS_INLINE KSystemResource &GetApplicationSystemResource() {
|
||||
return s_app_system_resource;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KMemoryBlockSlabManager &GetSystemMemoryBlockManager() {
|
||||
return s_sys_memory_block_manager;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KBlockInfoManager &GetApplicationBlockInfoManager() {
|
||||
return s_app_block_info_manager;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KBlockInfoManager &GetSystemBlockInfoManager() {
|
||||
return s_sys_block_info_manager;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPageTableManager &GetApplicationPageTableManager() {
|
||||
return s_app_page_table_manager;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPageTableManager &GetSystemPageTableManager() {
|
||||
return s_sys_page_table_manager;
|
||||
static ALWAYS_INLINE KSystemResource &GetSystemSystemResource() {
|
||||
return s_sys_system_resource;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KSupervisorPageTable &GetKernelPageTable() {
|
||||
|
||||
@@ -64,11 +64,13 @@ namespace ams::kern {
|
||||
static size_t GetNumRemaining() { return s_slab_heap.GetNumRemaining(); }
|
||||
};
|
||||
|
||||
template<typename Derived, typename Base, bool SupportDynamicExpansion = false> requires std::derived_from<Base, KAutoObjectWithList>
|
||||
class KAutoObjectWithSlabHeapAndContainer : public Base {
|
||||
template<typename Derived, typename Base, bool SupportDynamicExpansion> requires std::derived_from<Base, KAutoObject>
|
||||
class KAutoObjectWithSlabHeapBase : public Base {
|
||||
private:
|
||||
template<typename, typename, bool> friend class KAutoObjectWithSlabHeap;
|
||||
template<typename, typename, bool> friend class KAutoObjectWithSlabHeapAndContainer;
|
||||
private:
|
||||
static constinit inline KSlabHeap<Derived, SupportDynamicExpansion> s_slab_heap;
|
||||
static constinit inline KAutoObjectWithListContainer<Derived> s_container;
|
||||
private:
|
||||
static ALWAYS_INLINE Derived *Allocate() {
|
||||
return s_slab_heap.Allocate();
|
||||
@@ -77,12 +79,6 @@ namespace ams::kern {
|
||||
static ALWAYS_INLINE void Free(Derived *obj) {
|
||||
s_slab_heap.Free(obj);
|
||||
}
|
||||
public:
|
||||
class ListAccessor : public KAutoObjectWithListContainer<Derived>::ListAccessor {
|
||||
public:
|
||||
ALWAYS_INLINE ListAccessor() : KAutoObjectWithListContainer<Derived>::ListAccessor(s_container) { /* ... */ }
|
||||
ALWAYS_INLINE ~ListAccessor() { /* ... */ }
|
||||
};
|
||||
private:
|
||||
static ALWAYS_INLINE bool IsInitialized(const Derived *obj) {
|
||||
if constexpr (requires { { obj->IsInitialized() } -> std::same_as<bool>; }) {
|
||||
@@ -100,9 +96,9 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
public:
|
||||
constexpr explicit KAutoObjectWithSlabHeapAndContainer(util::ConstantInitializeTag) : Base(util::ConstantInitialize) { /* ... */ }
|
||||
constexpr explicit KAutoObjectWithSlabHeapBase(util::ConstantInitializeTag) : Base(util::ConstantInitialize) { /* ... */ }
|
||||
|
||||
explicit KAutoObjectWithSlabHeapAndContainer() { /* ... */ }
|
||||
explicit KAutoObjectWithSlabHeapBase() { /* ... */ }
|
||||
|
||||
/* NOTE: IsInitialized() and GetPostDestroyArgument() are virtual functions declared in this class, */
|
||||
/* in Nintendo's kernel. We fully devirtualize them, as Destroy() is the only user of them. */
|
||||
@@ -110,14 +106,14 @@ namespace ams::kern {
|
||||
virtual void Destroy() override final {
|
||||
Derived * const derived = static_cast<Derived *>(this);
|
||||
|
||||
if (IsInitialized(derived)) {
|
||||
s_container.Unregister(derived);
|
||||
const uintptr_t arg = GetPostDestroyArgument(derived);
|
||||
if (KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>::IsInitialized(derived)) {
|
||||
Derived::PreFinalize(derived);
|
||||
const uintptr_t arg = KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>::GetPostDestroyArgument(derived);
|
||||
derived->Finalize();
|
||||
Free(derived);
|
||||
KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>::Free(derived);
|
||||
Derived::PostDestroy(arg);
|
||||
} else {
|
||||
Free(derived);
|
||||
KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>::Free(derived);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +123,6 @@ namespace ams::kern {
|
||||
public:
|
||||
static void InitializeSlabHeap(void *memory, size_t memory_size) {
|
||||
s_slab_heap.Initialize(memory, memory_size);
|
||||
s_container.Initialize();
|
||||
}
|
||||
|
||||
static Derived *Create() {
|
||||
@@ -150,10 +145,6 @@ namespace ams::kern {
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void Register(Derived *obj) {
|
||||
return s_container.Register(obj);
|
||||
}
|
||||
|
||||
static size_t GetObjectSize() { return s_slab_heap.GetObjectSize(); }
|
||||
static size_t GetSlabHeapSize() { return s_slab_heap.GetSlabHeapSize(); }
|
||||
static size_t GetPeakIndex() { return s_slab_heap.GetPeakIndex(); }
|
||||
@@ -162,4 +153,42 @@ namespace ams::kern {
|
||||
static size_t GetNumRemaining() { return s_slab_heap.GetNumRemaining(); }
|
||||
};
|
||||
|
||||
template<typename Derived, typename Base, bool SupportDynamicExpansion = false>
|
||||
class KAutoObjectWithSlabHeap : public KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion> {
|
||||
public:
|
||||
constexpr explicit KAutoObjectWithSlabHeap(util::ConstantInitializeTag) : KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>(util::ConstantInitialize) { /* ... */ }
|
||||
|
||||
explicit KAutoObjectWithSlabHeap() { /* ... */ }
|
||||
|
||||
static ALWAYS_INLINE void PreFinalize(Derived *) { /* ... */ }
|
||||
};
|
||||
|
||||
|
||||
template<typename Derived, typename Base, bool SupportDynamicExpansion = false> requires std::derived_from<Base, KAutoObjectWithList>
|
||||
class KAutoObjectWithSlabHeapAndContainer : public KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion> {
|
||||
private:
|
||||
static constinit inline KAutoObjectWithListContainer<Derived> s_container;
|
||||
public:
|
||||
class ListAccessor : public KAutoObjectWithListContainer<Derived>::ListAccessor {
|
||||
public:
|
||||
ALWAYS_INLINE ListAccessor() : KAutoObjectWithListContainer<Derived>::ListAccessor(s_container) { /* ... */ }
|
||||
ALWAYS_INLINE ~ListAccessor() { /* ... */ }
|
||||
};
|
||||
public:
|
||||
constexpr explicit KAutoObjectWithSlabHeapAndContainer(util::ConstantInitializeTag) : KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>(util::ConstantInitialize) { /* ... */ }
|
||||
|
||||
explicit KAutoObjectWithSlabHeapAndContainer() { /* ... */ }
|
||||
public:
|
||||
static void InitializeSlabHeap(void *memory, size_t memory_size) {
|
||||
KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion>::InitializeSlabHeap(memory, memory_size);
|
||||
s_container.Initialize();
|
||||
}
|
||||
|
||||
static void Register(Derived *obj) {
|
||||
return s_container.Register(obj);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void PreFinalize(Derived *obj) { s_container.Unregister(obj); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,25 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
ALWAYS_INLINE ~KScopedCoreMigrationDisable() { GetCurrentThread().EnableCoreMigration(); }
|
||||
};
|
||||
|
||||
class KScopedCacheMaintenance {
|
||||
private:
|
||||
bool m_active;
|
||||
public:
|
||||
ALWAYS_INLINE KScopedCacheMaintenance() {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
if (m_active = !GetCurrentThread().IsInCacheMaintenanceOperation(); m_active) {
|
||||
GetCurrentThread().SetInCacheMaintenanceOperation();
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ~KScopedCacheMaintenance() {
|
||||
if (m_active) {
|
||||
GetCurrentThread().ClearInCacheMaintenanceOperation();
|
||||
}
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
};
|
||||
|
||||
/* Nintendo registers a handler for a SGI on thread termination, but does not handle anything. */
|
||||
/* This is sufficient, because post-interrupt scheduling is all they really intend to occur. */
|
||||
class KThreadTerminationInterruptHandler : public KInterruptHandler {
|
||||
@@ -432,9 +451,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
Result InvalidateDataCache(void *addr, size_t size) {
|
||||
/* Mark ourselves as in a cache maintenance operation, and prevent re-ordering. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
GetCurrentThread().SetInCacheMaintenanceOperation();
|
||||
ON_SCOPE_EXIT { GetCurrentThread().ClearInCacheMaintenanceOperation(); __asm__ __volatile__("" ::: "memory"); };
|
||||
KScopedCacheMaintenance cm;
|
||||
|
||||
const uintptr_t start = reinterpret_cast<uintptr_t>(addr);
|
||||
const uintptr_t end = start + size;
|
||||
@@ -460,9 +477,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
Result StoreDataCache(const void *addr, size_t size) {
|
||||
/* Mark ourselves as in a cache maintenance operation, and prevent re-ordering. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
GetCurrentThread().SetInCacheMaintenanceOperation();
|
||||
ON_SCOPE_EXIT { GetCurrentThread().ClearInCacheMaintenanceOperation(); __asm__ __volatile__("" ::: "memory"); };
|
||||
KScopedCacheMaintenance cm;
|
||||
|
||||
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), DataCacheLineSize);
|
||||
const uintptr_t end = util::AlignUp( reinterpret_cast<uintptr_t>(addr) + size, DataCacheLineSize);
|
||||
@@ -472,9 +487,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
Result FlushDataCache(const void *addr, size_t size) {
|
||||
/* Mark ourselves as in a cache maintenance operation, and prevent re-ordering. */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
GetCurrentThread().SetInCacheMaintenanceOperation();
|
||||
ON_SCOPE_EXIT { GetCurrentThread().ClearInCacheMaintenanceOperation(); __asm__ __volatile__("" ::: "memory"); };
|
||||
KScopedCacheMaintenance cm;
|
||||
|
||||
const uintptr_t start = util::AlignDown(reinterpret_cast<uintptr_t>(addr), DataCacheLineSize);
|
||||
const uintptr_t end = util::AlignUp( reinterpret_cast<uintptr_t>(addr) + size, DataCacheLineSize);
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace ams::kern::arch::arm64 {
|
||||
Result KPageTable::InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end) {
|
||||
/* Initialize basic fields. */
|
||||
m_asid = 0;
|
||||
m_manager = std::addressof(Kernel::GetSystemPageTableManager());
|
||||
m_manager = Kernel::GetSystemSystemResource().GetPageTableManagerPointer();
|
||||
|
||||
/* Allocate a page for ttbr. */
|
||||
/* NOTE: It is a postcondition of page table manager allocation that the page is all-zero. */
|
||||
@@ -207,7 +207,7 @@ namespace ams::kern::arch::arm64 {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager, KResourceLimit *resource_limit) {
|
||||
Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource *system_resource, KResourceLimit *resource_limit) {
|
||||
/* The input ID isn't actually used. */
|
||||
MESOSPHERE_UNUSED(id);
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace ams::kern::arch::arm64 {
|
||||
ON_RESULT_FAILURE { g_asid_manager.Release(m_asid); };
|
||||
|
||||
/* Set our manager. */
|
||||
m_manager = pt_manager;
|
||||
m_manager = system_resource->GetPageTableManagerPointer();
|
||||
|
||||
/* Allocate a new table, and set our ttbr value. */
|
||||
const KVirtualAddress new_table = m_manager->Allocate();
|
||||
@@ -228,7 +228,7 @@ namespace ams::kern::arch::arm64 {
|
||||
const size_t as_width = GetAddressSpaceWidth(as_type);
|
||||
const KProcessAddress as_start = 0;
|
||||
const KProcessAddress as_end = (1ul << as_width);
|
||||
R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager, resource_limit));
|
||||
R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, enable_das_merge, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, system_resource, resource_limit));
|
||||
|
||||
/* Note that we've updated the table (since we created it). */
|
||||
this->NoteUpdated();
|
||||
@@ -348,7 +348,7 @@ namespace ams::kern::arch::arm64 {
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(virt_addr), PageSize));
|
||||
MESOSPHERE_ASSERT(this->ContainsPages(virt_addr, num_pages));
|
||||
|
||||
if (operation == OperationType_Map) {
|
||||
if (operation == OperationType_Map || operation == OperationType_MapFirst) {
|
||||
MESOSPHERE_ABORT_UNLESS(is_pa_valid);
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize));
|
||||
} else {
|
||||
@@ -357,16 +357,32 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
if (operation == OperationType_Unmap) {
|
||||
R_RETURN(this->Unmap(virt_addr, num_pages, page_list, false, reuse_ll));
|
||||
} else if (operation == OperationType_Separate) {
|
||||
const size_t size = num_pages * PageSize;
|
||||
R_TRY(this->SeparatePages(virt_addr, std::min(util::GetAlignment(GetInteger(virt_addr)), size), page_list, reuse_ll));
|
||||
ON_RESULT_FAILURE { this->MergePages(virt_addr, page_list); };
|
||||
|
||||
if (num_pages > 1) {
|
||||
const auto end_page = virt_addr + size;
|
||||
const auto last_page = end_page - PageSize;
|
||||
|
||||
R_TRY(this->SeparatePages(last_page, std::min(util::GetAlignment(GetInteger(end_page)), size), page_list, reuse_ll));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
auto entry_template = this->GetEntryTemplate(properties);
|
||||
|
||||
switch (operation) {
|
||||
case OperationType_Map:
|
||||
R_RETURN(this->MapContiguous(virt_addr, phys_addr, num_pages, entry_template, properties.disable_merge_attributes == DisableMergeAttribute_DisableHead, page_list, reuse_ll));
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -740,7 +756,7 @@ namespace ams::kern::arch::arm64 {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KPageTable::MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, PageLinkedList *page_list, bool reuse_ll) {
|
||||
Result KPageTable::MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, bool disable_head_merge, bool not_first, PageLinkedList *page_list, bool reuse_ll) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
/* Cache initial addresses for use on cleanup. */
|
||||
@@ -811,7 +827,11 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
/* Open references to the pages, if we should. */
|
||||
if (IsHeapPhysicalAddress(orig_phys_addr)) {
|
||||
Kernel::GetMemoryManager().Open(orig_phys_addr, num_pages);
|
||||
if (not_first) {
|
||||
Kernel::GetMemoryManager().Open(orig_phys_addr, num_pages);
|
||||
} else {
|
||||
Kernel::GetMemoryManager().OpenFirst(orig_phys_addr, num_pages);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -1215,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. */
|
||||
@@ -1433,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]
|
||||
|
||||
@@ -86,9 +86,10 @@ _ZN3ams4kern3svc14RestoreContextEm:
|
||||
/* Get our exception flags. */
|
||||
ldrb w9, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_EXCEPTION_FLAGS)]
|
||||
|
||||
/* Clear in-svc and needs-fpu-restore flags. */
|
||||
/* Clear in-svc, in-user-exception, and needs-fpu-restore flags. */
|
||||
and w10, w9, #(~(THREAD_EXCEPTION_FLAG_IS_FPU_CONTEXT_RESTORE_NEEDED))
|
||||
and w10, w10, #(~(THREAD_EXCEPTION_FLAG_IS_CALLING_SVC))
|
||||
and w10, w10, #(~(THREAD_EXCEPTION_FLAG_IS_IN_USERMODE_EXCEPTION_HANDLER))
|
||||
strb w10, [sp, #(EXCEPTION_CONTEXT_SIZE + THREAD_STACK_PARAMETERS_EXCEPTION_FLAGS)]
|
||||
|
||||
/* If we don't need to restore the fpu, skip restoring it. */
|
||||
|
||||
@@ -651,11 +651,13 @@ namespace ams::kern::board::nintendo::nx {
|
||||
g_memory_controller_address = KMemoryLayout::GetDevicePhysicalAddress(KMemoryRegionType_MemoryController);
|
||||
|
||||
/* Allocate a page to use as a reserved/no device table. */
|
||||
const KVirtualAddress table_virt_addr = Kernel::GetSystemPageTableManager().Allocate();
|
||||
auto &ptm = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
|
||||
const KVirtualAddress table_virt_addr = ptm.Allocate();
|
||||
MESOSPHERE_ABORT_UNLESS(table_virt_addr != Null<KVirtualAddress>);
|
||||
const KPhysicalAddress table_phys_addr = GetPageTablePhysicalAddress(table_virt_addr);
|
||||
MESOSPHERE_ASSERT(IsValidPhysicalAddress(table_phys_addr));
|
||||
Kernel::GetSystemPageTableManager().Open(table_virt_addr, 1);
|
||||
ptm.Open(table_virt_addr, 1);
|
||||
|
||||
/* Save the page. Note that it is a pre-condition that the page is cleared, when allocated from the system page table manager. */
|
||||
/* NOTE: Nintendo does not check the result of StoreDataCache. */
|
||||
@@ -779,7 +781,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
const size_t end_index = (space_address + space_size - 1) / DeviceRegionSize;
|
||||
|
||||
/* Get the page table manager. */
|
||||
auto &ptm = Kernel::GetSystemPageTableManager();
|
||||
auto &ptm = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
|
||||
/* Clear the tables. */
|
||||
static_assert(TableCount == (1ul << DeviceVirtualAddressBits) / DeviceRegionSize);
|
||||
@@ -839,7 +841,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
void KDevicePageTable::Finalize() {
|
||||
/* Get the page table manager. */
|
||||
auto &ptm = Kernel::GetSystemPageTableManager();
|
||||
auto &ptm = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
|
||||
/* Detach from all devices. */
|
||||
{
|
||||
@@ -1014,7 +1016,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* Get the memory manager and page table manager. */
|
||||
KMemoryManager &mm = Kernel::GetMemoryManager();
|
||||
KPageTableManager &ptm = Kernel::GetSystemPageTableManager();
|
||||
KPageTableManager &ptm = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
|
||||
/* Cache permissions. */
|
||||
const bool read = (device_perm & ams::svc::MemoryPermission_Read) != 0;
|
||||
@@ -1116,6 +1118,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
}
|
||||
|
||||
Result KDevicePageTable::MapImpl(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
|
||||
|
||||
/* Ensure that the region we're mapping to is free. */
|
||||
R_UNLESS(this->IsFree(device_address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
@@ -1158,10 +1161,10 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* Get the memory manager and page table manager. */
|
||||
KMemoryManager &mm = Kernel::GetMemoryManager();
|
||||
KPageTableManager &ptm = Kernel::GetSystemPageTableManager();
|
||||
KPageTableManager &ptm = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
|
||||
/* Make a page group for the pages we're closing. */
|
||||
KPageGroup pg(std::addressof(Kernel::GetSystemBlockInfoManager()));
|
||||
KPageGroup pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer());
|
||||
|
||||
/* Walk the directory. */
|
||||
u64 remaining = size;
|
||||
@@ -1414,11 +1417,15 @@ namespace ams::kern::board::nintendo::nx {
|
||||
return true;
|
||||
}
|
||||
|
||||
Result KDevicePageTable::Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
|
||||
Result KDevicePageTable::Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool is_io) {
|
||||
/* Validate address/size. */
|
||||
MESOSPHERE_ASSERT((device_address & ~DeviceVirtualAddressMask) == 0);
|
||||
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||
|
||||
/* IO is not supported on NX board. */
|
||||
MESOSPHERE_ASSERT(!is_io);
|
||||
MESOSPHERE_UNUSED(is_io);
|
||||
|
||||
/* Map the pages. */
|
||||
R_RETURN(this->MapImpl(page_table, process_address, size, device_address, device_perm, is_aligned));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace ams::kern::board::nintendo::nx {
|
||||
constinit bool g_call_smc_on_panic;
|
||||
|
||||
/* Global variables for secure memory. */
|
||||
constexpr size_t SecureAppletMemorySize = 4_MB;
|
||||
constinit KSpinLock g_secure_applet_lock;
|
||||
constinit bool g_secure_applet_memory_used = false;
|
||||
constinit KVirtualAddress g_secure_applet_memory_address = Null<KVirtualAddress>;
|
||||
@@ -246,8 +245,8 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
Result AllocateSecureMemoryForApplet(KVirtualAddress *out, size_t size) {
|
||||
/* Verify that the size is valid. */
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size <= SecureAppletMemorySize, svc::ResultOutOfMemory());
|
||||
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
|
||||
R_UNLESS(size <= KSystemControl::SecureAppletMemorySize, svc::ResultOutOfMemory());
|
||||
|
||||
/* Disable interrupts and acquire the secure applet lock. */
|
||||
KScopedInterruptDisable di;
|
||||
@@ -273,7 +272,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* Verify that the memory being freed is correct. */
|
||||
MESOSPHERE_ABORT_UNLESS(address == g_secure_applet_memory_address);
|
||||
MESOSPHERE_ABORT_UNLESS(size <= SecureAppletMemorySize);
|
||||
MESOSPHERE_ABORT_UNLESS(size <= KSystemControl::SecureAppletMemorySize);
|
||||
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
|
||||
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_used);
|
||||
|
||||
@@ -368,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() {
|
||||
@@ -383,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);
|
||||
}
|
||||
|
||||
@@ -399,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. */
|
||||
@@ -442,26 +446,17 @@ 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() {
|
||||
/* Initialize the sleep manager. */
|
||||
KSleepManager::Initialize();
|
||||
|
||||
/* Reserve secure applet memory. */
|
||||
if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
|
||||
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null<KVirtualAddress>);
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize));
|
||||
/* Get the secure applet memory. */
|
||||
const auto &secure_applet_memory = KMemoryLayout::GetSecureAppletMemoryRegion();
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(secure_applet_memory.GetSize() == SecureAppletMemorySize);
|
||||
|
||||
constexpr auto SecureAppletAllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
|
||||
const KPhysicalAddress secure_applet_memory_phys_addr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(SecureAppletMemorySize / PageSize, 1, SecureAppletAllocateOption);
|
||||
MESOSPHERE_ABORT_UNLESS(secure_applet_memory_phys_addr != Null<KPhysicalAddress>);
|
||||
|
||||
g_secure_applet_memory_address = KMemoryLayout::GetLinearVirtualAddress(secure_applet_memory_phys_addr);
|
||||
}
|
||||
g_secure_applet_memory_address = secure_applet_memory.GetAddress();
|
||||
|
||||
/* Initialize KTrace (and potentially other init). */
|
||||
KSystemControlBase::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();
|
||||
|
||||
}
|
||||
|
||||
@@ -17,30 +17,36 @@
|
||||
|
||||
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
|
||||
|
||||
#define FOREACH_SLAB_TYPE(HANDLER, ...) \
|
||||
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \
|
||||
HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
|
||||
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \
|
||||
HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
|
||||
HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
|
||||
HANDLER(KPort, (SLAB_COUNT(KPort)), ## __VA_ARGS__) \
|
||||
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ## __VA_ARGS__) \
|
||||
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ## __VA_ARGS__) \
|
||||
HANDLER(KSession, (SLAB_COUNT(KSession)), ## __VA_ARGS__) \
|
||||
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ## __VA_ARGS__) \
|
||||
HANDLER(KLightSession, (SLAB_COUNT(KLightSession)), ## __VA_ARGS__) \
|
||||
HANDLER(KThreadLocalPage, (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), ## __VA_ARGS__) \
|
||||
HANDLER(KObjectName, (SLAB_COUNT(KObjectName)), ## __VA_ARGS__) \
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ## __VA_ARGS__) \
|
||||
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
|
||||
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
|
||||
HANDLER(KIoPool, (SLAB_COUNT(KIoPool)), ## __VA_ARGS__) \
|
||||
HANDLER(KIoRegion, (SLAB_COUNT(KIoRegion)), ## __VA_ARGS__)
|
||||
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \
|
||||
HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
|
||||
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \
|
||||
HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
|
||||
HANDLER(KPort, (SLAB_COUNT(KPort)), ## __VA_ARGS__) \
|
||||
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ## __VA_ARGS__) \
|
||||
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ## __VA_ARGS__) \
|
||||
HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ## __VA_ARGS__) \
|
||||
HANDLER(KSession, (SLAB_COUNT(KSession)), ## __VA_ARGS__) \
|
||||
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ## __VA_ARGS__) \
|
||||
HANDLER(KLightSession, (SLAB_COUNT(KLightSession)), ## __VA_ARGS__) \
|
||||
HANDLER(KThreadLocalPage, (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), ## __VA_ARGS__) \
|
||||
HANDLER(KObjectName, (SLAB_COUNT(KObjectName)), ## __VA_ARGS__) \
|
||||
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ## __VA_ARGS__) \
|
||||
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ## __VA_ARGS__) \
|
||||
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(KThreadLockInfo, (SLAB_COUNT(KThread)), ## __VA_ARGS__)
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -55,50 +61,55 @@ namespace ams::kern::init {
|
||||
#undef DEFINE_SLAB_TYPE_ENUM_MEMBER
|
||||
|
||||
/* Constexpr counts. */
|
||||
constexpr size_t SlabCountKProcess = 80;
|
||||
constexpr size_t SlabCountKThread = 800;
|
||||
constexpr size_t SlabCountKEvent = 900;
|
||||
constexpr size_t SlabCountKInterruptEvent = 100;
|
||||
constexpr size_t SlabCountKPort = 384;
|
||||
constexpr size_t SlabCountKSharedMemory = 80;
|
||||
constexpr size_t SlabCountKTransferMemory = 200;
|
||||
constexpr size_t SlabCountKCodeMemory = 10;
|
||||
constexpr size_t SlabCountKDeviceAddressSpace = 300;
|
||||
constexpr size_t SlabCountKSession = 1133;
|
||||
constexpr size_t SlabCountKLightSession = 100;
|
||||
constexpr size_t SlabCountKObjectName = 7;
|
||||
constexpr size_t SlabCountKResourceLimit = 5;
|
||||
constexpr size_t SlabCountKDebug = cpu::NumCores;
|
||||
constexpr size_t SlabCountKIoPool = 1;
|
||||
constexpr size_t SlabCountKIoRegion = 6;
|
||||
constexpr size_t SlabCountKProcess = 80;
|
||||
constexpr size_t SlabCountKThread = 800;
|
||||
constexpr size_t SlabCountKEvent = 900;
|
||||
constexpr size_t SlabCountKInterruptEvent = 100;
|
||||
constexpr size_t SlabCountKPort = 384;
|
||||
constexpr size_t SlabCountKSharedMemory = 80;
|
||||
constexpr size_t SlabCountKTransferMemory = 200;
|
||||
constexpr size_t SlabCountKCodeMemory = 10;
|
||||
constexpr size_t SlabCountKDeviceAddressSpace = 300;
|
||||
constexpr size_t SlabCountKSession = 1133;
|
||||
constexpr size_t SlabCountKLightSession = 100;
|
||||
constexpr size_t SlabCountKObjectName = 7;
|
||||
constexpr size_t SlabCountKResourceLimit = 5;
|
||||
constexpr size_t SlabCountKDebug = cpu::NumCores;
|
||||
constexpr size_t SlabCountKIoPool = 1;
|
||||
constexpr size_t SlabCountKIoRegion = 6;
|
||||
constexpr size_t SlabcountKSessionRequestMappings = 40;
|
||||
|
||||
constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
|
||||
constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
|
||||
|
||||
namespace test {
|
||||
|
||||
constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo));
|
||||
static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize);
|
||||
|
||||
static_assert(KernelPageBufferHeapSize == 2 * PageSize + (SlabCountKProcess + SlabCountKThread + (SlabCountKProcess + SlabCountKThread) / 8) * PageSize);
|
||||
static_assert(KernelPageBufferAdditionalSize == (SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);
|
||||
|
||||
}
|
||||
|
||||
/* Global to hold our resource counts. */
|
||||
constinit KSlabResourceCounts g_slab_resource_counts = {
|
||||
.num_KProcess = SlabCountKProcess,
|
||||
.num_KThread = SlabCountKThread,
|
||||
.num_KEvent = SlabCountKEvent,
|
||||
.num_KInterruptEvent = SlabCountKInterruptEvent,
|
||||
.num_KPort = SlabCountKPort,
|
||||
.num_KSharedMemory = SlabCountKSharedMemory,
|
||||
.num_KTransferMemory = SlabCountKTransferMemory,
|
||||
.num_KCodeMemory = SlabCountKCodeMemory,
|
||||
.num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
|
||||
.num_KSession = SlabCountKSession,
|
||||
.num_KLightSession = SlabCountKLightSession,
|
||||
.num_KObjectName = SlabCountKObjectName,
|
||||
.num_KResourceLimit = SlabCountKResourceLimit,
|
||||
.num_KDebug = SlabCountKDebug,
|
||||
.num_KIoPool = SlabCountKIoPool,
|
||||
.num_KIoRegion = SlabCountKIoRegion,
|
||||
.num_KProcess = SlabCountKProcess,
|
||||
.num_KThread = SlabCountKThread,
|
||||
.num_KEvent = SlabCountKEvent,
|
||||
.num_KInterruptEvent = SlabCountKInterruptEvent,
|
||||
.num_KPort = SlabCountKPort,
|
||||
.num_KSharedMemory = SlabCountKSharedMemory,
|
||||
.num_KTransferMemory = SlabCountKTransferMemory,
|
||||
.num_KCodeMemory = SlabCountKCodeMemory,
|
||||
.num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
|
||||
.num_KSession = SlabCountKSession,
|
||||
.num_KLightSession = SlabCountKLightSession,
|
||||
.num_KObjectName = SlabCountKObjectName,
|
||||
.num_KResourceLimit = SlabCountKResourceLimit,
|
||||
.num_KDebug = SlabCountKDebug,
|
||||
.num_KIoPool = SlabCountKIoPool,
|
||||
.num_KIoRegion = SlabCountKIoRegion,
|
||||
.num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -131,7 +142,7 @@ namespace ams::kern::init {
|
||||
}
|
||||
|
||||
size_t CalculateSlabHeapGapSize() {
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MB - 296_KB;
|
||||
constexpr size_t KernelSlabHeapGapSize = 2_MB - 356_KB;
|
||||
static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
|
||||
return KernelSlabHeapGapSize;
|
||||
}
|
||||
@@ -155,23 +166,6 @@ namespace ams::kern::init {
|
||||
return size;
|
||||
}
|
||||
|
||||
void InitializeKPageBufferSlabHeap() {
|
||||
const auto &counts = GetSlabResourceCounts();
|
||||
const size_t num_pages = counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
const size_t slab_size = num_pages * PageSize;
|
||||
|
||||
/* Reserve memory from the system resource limit. */
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, slab_size));
|
||||
|
||||
/* Allocate memory for the slab. */
|
||||
constexpr auto AllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
|
||||
const KPhysicalAddress slab_address = Kernel::GetMemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
|
||||
MESOSPHERE_ABORT_UNLESS(slab_address != Null<KPhysicalAddress>);
|
||||
|
||||
/* Initialize the slabheap. */
|
||||
KPageBuffer::InitializeSlabHeap(GetVoidPointer(KMemoryLayout::GetLinearVirtualAddress(slab_address)), slab_size);
|
||||
}
|
||||
|
||||
void InitializeSlabHeaps() {
|
||||
/* Get the slab region, since that's where we'll be working. */
|
||||
const KMemoryRegion &slab_region = KMemoryLayout::GetSlabRegion();
|
||||
@@ -240,4 +234,34 @@ namespace ams::kern::init {
|
||||
FreeUnusedSlabMemory(gap_start, gap_size + (slab_region.GetEndAddress() - GetInteger(address)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
void KPageBufferSlabHeap::Initialize(KDynamicPageManager &allocator) {
|
||||
/* Get slab resource counts. */
|
||||
const auto &counts = init::GetSlabResourceCounts();
|
||||
|
||||
/* If size is correct, account for thread local pages. */
|
||||
if (BufferSize == PageSize) {
|
||||
s_buffer_count += counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
|
||||
}
|
||||
|
||||
/* Set our object size. */
|
||||
m_obj_size = BufferSize;
|
||||
|
||||
/* Initialize the base allocator. */
|
||||
KSlabHeapImpl::Initialize();
|
||||
|
||||
/* Allocate the desired page count. */
|
||||
for (size_t i = 0; i < s_buffer_count; ++i) {
|
||||
/* Allocate an appropriate buffer. */
|
||||
auto * const pb = (BufferSize <= PageSize) ? allocator.Allocate() : allocator.Allocate(BufferSize / PageSize);
|
||||
MESOSPHERE_ABORT_UNLESS(pb != nullptr);
|
||||
|
||||
/* Free to our slab. */
|
||||
KSlabHeapImpl::Free(pb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -128,13 +128,13 @@ namespace ams::kern {
|
||||
KProcess *new_process = nullptr;
|
||||
{
|
||||
/* Make page groups to represent the data. */
|
||||
KPageGroup pg(std::addressof(Kernel::GetSystemBlockInfoManager()));
|
||||
KPageGroup workaround_pg(std::addressof(Kernel::GetSystemBlockInfoManager()));
|
||||
KPageGroup pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer());
|
||||
KPageGroup workaround_pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer());
|
||||
|
||||
/* Populate the page group to represent the data. */
|
||||
{
|
||||
/* Allocate the previously unreserved pages. */
|
||||
KPageGroup unreserve_pg(std::addressof(Kernel::GetSystemBlockInfoManager()));
|
||||
KPageGroup unreserve_pg(Kernel::GetSystemSystemResource().GetBlockInfoManagerPointer());
|
||||
MESOSPHERE_R_ABORT_UNLESS(Kernel::GetMemoryManager().AllocateAndOpen(std::addressof(unreserve_pg), unreserved_size / PageSize, KMemoryManager::EncodeOption(dst_pool, KMemoryManager::Direction_FromFront)));
|
||||
|
||||
/* Add the previously reserved pages. */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ namespace ams::kern {
|
||||
static_assert(ClassToken<KDeviceAddressSpace> == 0b10010001'00000000);
|
||||
static_assert(ClassToken<KSessionRequest> == 0b01100001'00000000);
|
||||
static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);
|
||||
static_assert(ClassToken<KIoPool> == 0b11000001'00000000);
|
||||
static_assert(ClassToken<KIoRegion> == 0b00001110'00000000);
|
||||
/* 0b00010110'00000000 */
|
||||
/* 0b00100110'00000000 */
|
||||
static_assert(ClassToken<KSystemResource> == 0b01000110'00000000);
|
||||
|
||||
|
||||
/* Ensure that the token hierarchy is correct. */
|
||||
|
||||
@@ -72,6 +78,10 @@ namespace ams::kern {
|
||||
static_assert(ClassToken<KDeviceAddressSpace> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KSessionRequest> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KIoPool> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
|
||||
static_assert(ClassToken<KIoRegion> == ((0b00001110 << 8) | ClassToken<KAutoObject>));
|
||||
|
||||
static_assert(ClassToken<KSystemResource> == ((0b01000110 << 8) | ClassToken<KAutoObject>));
|
||||
|
||||
/* Ensure that the token hierarchy reflects the class hierarchy. */
|
||||
|
||||
@@ -100,6 +110,10 @@ namespace ams::kern {
|
||||
static_assert(std::is_final<KDeviceAddressSpace>::value && std::is_base_of<KAutoObject, KDeviceAddressSpace>::value);
|
||||
static_assert(std::is_final<KSessionRequest>::value && std::is_base_of<KAutoObject, KSessionRequest>::value);
|
||||
static_assert(std::is_final<KCodeMemory>::value && std::is_base_of<KAutoObject, KCodeMemory>::value);
|
||||
static_assert(std::is_final<KIoPool>::value && std::is_base_of<KAutoObject, KIoPool>::value);
|
||||
static_assert(std::is_final<KIoRegion>::value && std::is_base_of<KAutoObject, KIoRegion>::value);
|
||||
|
||||
static_assert(std::is_base_of<KAutoObject, KSystemResource>::value);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -61,10 +61,21 @@ namespace ams::kern {
|
||||
R_RETURN(m_table.Detach(device_name));
|
||||
}
|
||||
|
||||
Result KDeviceAddressSpace::Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
|
||||
Result KDeviceAddressSpace::Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, u32 option, bool is_aligned) {
|
||||
/* Check that the address falls within the space. */
|
||||
R_UNLESS((m_space_address <= device_address && device_address + size - 1 <= m_space_address + m_space_size - 1), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Decode the option. */
|
||||
const util::BitPack32 option_pack = { option };
|
||||
const auto device_perm = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Permission>();
|
||||
const auto flags = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Flags>();
|
||||
const auto reserved = option_pack.Get<ams::svc::MapDeviceAddressSpaceOption::Reserved>();
|
||||
|
||||
/* Validate the option. */
|
||||
/* TODO: It is likely that this check for flags == none is only on NX board. */
|
||||
R_UNLESS(flags == ams::svc::MapDeviceAddressSpaceFlag_None, svc::ResultInvalidEnumValue());
|
||||
R_UNLESS(reserved == 0, svc::ResultInvalidEnumValue());
|
||||
|
||||
/* Lock the address space. */
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
@@ -72,15 +83,21 @@ namespace ams::kern {
|
||||
KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
|
||||
|
||||
/* Lock the pages. */
|
||||
R_TRY(page_table->LockForMapDeviceAddressSpace(process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned));
|
||||
bool is_io{};
|
||||
R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size, ConvertToKMemoryPermission(device_perm), is_aligned, true));
|
||||
|
||||
/* Ensure that if we fail, we don't keep unmapped pages locked. */
|
||||
ON_RESULT_FAILURE { MESOSPHERE_R_ABORT_UNLESS(page_table->UnlockForDeviceAddressSpace(process_address, size)); };
|
||||
|
||||
/* Check that the io status is allowable. */
|
||||
if (is_io) {
|
||||
R_UNLESS((flags & ams::svc::MapDeviceAddressSpaceFlag_NotIoRegister) == 0, svc::ResultInvalidCombination());
|
||||
}
|
||||
|
||||
/* Map the pages. */
|
||||
{
|
||||
/* Perform the mapping. */
|
||||
R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm, is_aligned));
|
||||
R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm, is_aligned, is_io));
|
||||
|
||||
/* Ensure that we unmap the pages if we fail to update the protections. */
|
||||
/* NOTE: Nintendo does not check the result of this unmap call. */
|
||||
@@ -105,7 +122,7 @@ namespace ams::kern {
|
||||
KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
|
||||
|
||||
/* Lock the pages. */
|
||||
R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size));
|
||||
R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));
|
||||
|
||||
/* Unmap the pages. */
|
||||
{
|
||||
|
||||
@@ -332,7 +332,6 @@ namespace ams::kern::KDumpObject {
|
||||
MESOSPHERE_RELEASE_LOG(#__OBJECT__ "\n"); \
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%3zu Peak=%3zu Max=%3zu\n", __OBJECT__::GetSlabHeapSize() - __OBJECT__::GetNumRemaining(), __OBJECT__::GetPeakIndex(), __OBJECT__::GetSlabHeapSize())
|
||||
|
||||
DUMP_KSLABOBJ(KPageBuffer);
|
||||
DUMP_KSLABOBJ(KEvent);
|
||||
DUMP_KSLABOBJ(KInterruptEvent);
|
||||
DUMP_KSLABOBJ(KProcess);
|
||||
@@ -362,24 +361,24 @@ namespace ams::kern::KDumpObject {
|
||||
/* Memory block slabs. */
|
||||
{
|
||||
MESOSPHERE_RELEASE_LOG("App Memory Block\n");
|
||||
auto &app = Kernel::GetApplicationMemoryBlockManager();
|
||||
auto &app = Kernel::GetApplicationSystemResource().GetMemoryBlockSlabManager();
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", app.GetUsed(), app.GetPeak(), app.GetCount());
|
||||
MESOSPHERE_RELEASE_LOG("Sys Memory Block\n");
|
||||
auto &sys = Kernel::GetSystemMemoryBlockManager();
|
||||
auto &sys = Kernel::GetSystemSystemResource().GetMemoryBlockSlabManager();
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", sys.GetUsed(), sys.GetPeak(), sys.GetCount());
|
||||
}
|
||||
|
||||
/* KBlockInfo slab. */
|
||||
{
|
||||
MESOSPHERE_RELEASE_LOG("KBlockInfo\n");
|
||||
auto &manager = Kernel::GetSystemBlockInfoManager();
|
||||
auto &manager = Kernel::GetSystemSystemResource().GetBlockInfoManager();
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", manager.GetUsed(), manager.GetPeak(), manager.GetCount());
|
||||
}
|
||||
|
||||
/* Page Table slab. */
|
||||
{
|
||||
MESOSPHERE_RELEASE_LOG("Page Table\n");
|
||||
auto &manager = Kernel::GetSystemPageTableManager();
|
||||
auto &manager = Kernel::GetSystemSystemResource().GetPageTableManager();
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", manager.GetUsed(), manager.GetPeak(), manager.GetCount());
|
||||
}
|
||||
}
|
||||
@@ -425,15 +424,17 @@ namespace ams::kern::KDumpObject {
|
||||
process_pts += pts;
|
||||
|
||||
MESOSPHERE_RELEASE_LOG("%-12s: PID=%3lu Thread %4d / Event %4d / PageTable %5zu\n", process->GetName(), process->GetId(), threads, events, pts);
|
||||
if (process->GetTotalSystemResourceSize() != 0) {
|
||||
if (const auto &system_resource = process->GetSystemResource(); system_resource.IsSecureResource()) {
|
||||
const auto &secure_resource = static_cast<const KSecureSystemResource &>(system_resource);
|
||||
|
||||
MESOSPHERE_RELEASE_LOG(" System Resource\n");
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetDynamicPageManager().GetUsed(), process->GetDynamicPageManager().GetPeak(), process->GetDynamicPageManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", secure_resource.GetDynamicPageManager().GetUsed(), secure_resource.GetDynamicPageManager().GetPeak(), secure_resource.GetDynamicPageManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Memory Block\n");
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetMemoryBlockSlabManager().GetUsed(), process->GetMemoryBlockSlabManager().GetPeak(), process->GetMemoryBlockSlabManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", secure_resource.GetMemoryBlockSlabManager().GetUsed(), secure_resource.GetMemoryBlockSlabManager().GetPeak(), secure_resource.GetMemoryBlockSlabManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Page Table\n");
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetPageTableManager().GetUsed(), process->GetPageTableManager().GetPeak(), process->GetPageTableManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", secure_resource.GetPageTableManager().GetUsed(), secure_resource.GetPageTableManager().GetPeak(), secure_resource.GetPageTableManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Block Info\n");
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetBlockInfoManager().GetUsed(), process->GetBlockInfoManager().GetPeak(), process->GetBlockInfoManager().GetCount());
|
||||
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", secure_resource.GetBlockInfoManager().GetUsed(), secure_resource.GetBlockInfoManager().GetPeak(), secure_resource.GetBlockInfoManager().GetCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,6 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit KLightLock g_interrupt_event_lock;
|
||||
constinit KInterruptEventTask *g_interrupt_event_task_table[KInterruptController::NumInterrupts] = {};
|
||||
|
||||
}
|
||||
|
||||
Result KInterruptEvent::Initialize(int32_t interrupt_name, ams::svc::InterruptType type) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
@@ -40,8 +33,8 @@ namespace ams::kern {
|
||||
/* Initialize readable event base. */
|
||||
KReadableEvent::Initialize(nullptr);
|
||||
|
||||
/* Try to register the task. */
|
||||
R_TRY(KInterruptEventTask::Register(m_interrupt_id, m_core_id, type == ams::svc::InterruptType_Level, this));
|
||||
/* Bind ourselves as the handler for our interrupt id. */
|
||||
R_TRY(Kernel::GetInterruptManager().BindHandler(this, m_interrupt_id, m_core_id, KInterruptController::PriorityLevel_High, true, type == ams::svc::InterruptType_Level));
|
||||
|
||||
/* Mark initialized. */
|
||||
m_is_initialized = true;
|
||||
@@ -51,7 +44,11 @@ namespace ams::kern {
|
||||
void KInterruptEvent::Finalize() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id, m_core_id);
|
||||
/* Unbind ourselves as the handler for our interrupt id. */
|
||||
Kernel::GetInterruptManager().UnbindHandler(m_interrupt_id, m_core_id);
|
||||
|
||||
/* Synchronize the unbind on all cores, before proceeding. */
|
||||
KDpcManager::Sync();
|
||||
|
||||
/* Perform inherited finalization. */
|
||||
KReadableEvent::Finalize();
|
||||
@@ -72,79 +69,19 @@ namespace ams::kern {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KInterruptEventTask::Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event) {
|
||||
/* Lock the task table. */
|
||||
KScopedLightLock lk(g_interrupt_event_lock);
|
||||
|
||||
/* Get a task for the id. */
|
||||
bool allocated = false;
|
||||
KInterruptEventTask *task = g_interrupt_event_task_table[interrupt_id];
|
||||
if (task != nullptr) {
|
||||
/* Check that there's not already an event for this task. */
|
||||
R_UNLESS(task->m_event == nullptr, svc::ResultBusy());
|
||||
} else {
|
||||
/* Allocate a new task. */
|
||||
task = KInterruptEventTask::Allocate();
|
||||
R_UNLESS(task != nullptr, svc::ResultOutOfResource());
|
||||
|
||||
allocated = true;
|
||||
}
|
||||
|
||||
/* Ensure that the task is cleaned up if anything goes wrong. */
|
||||
ON_RESULT_FAILURE { if (allocated) { KInterruptEventTask::Free(task); } };
|
||||
|
||||
/* Register/bind the interrupt task. */
|
||||
{
|
||||
/* Lock the scheduler. */
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Bind the interrupt handler. */
|
||||
R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, core_id, KInterruptController::PriorityLevel_High, true, level));
|
||||
|
||||
/* Set the event. */
|
||||
task->m_event = event;
|
||||
}
|
||||
|
||||
/* If we allocated, set the event in the table. */
|
||||
if (allocated) {
|
||||
g_interrupt_event_task_table[interrupt_id] = task;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KInterruptEventTask::Unregister(s32 interrupt_id, s32 core_id) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Lock the task table. */
|
||||
KScopedLightLock lk(g_interrupt_event_lock);
|
||||
|
||||
/* Lock the scheduler. */
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
/* Ensure we can unregister. */
|
||||
MESOSPHERE_ABORT_UNLESS(g_interrupt_event_task_table[interrupt_id] == this);
|
||||
MESOSPHERE_ABORT_UNLESS(m_event != nullptr);
|
||||
|
||||
/* Unbind the interrupt. */
|
||||
m_event = nullptr;
|
||||
Kernel::GetInterruptManager().UnbindHandler(interrupt_id, core_id);
|
||||
}
|
||||
|
||||
KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) {
|
||||
KInterruptTask *KInterruptEvent::OnInterrupt(s32 interrupt_id) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_UNUSED(interrupt_id);
|
||||
return this;
|
||||
}
|
||||
|
||||
void KInterruptEventTask::DoTask() {
|
||||
void KInterruptEvent::DoTask() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Lock the scheduler. */
|
||||
KScopedSchedulerLock sl;
|
||||
|
||||
if (m_event != nullptr) {
|
||||
m_event->Signal();
|
||||
}
|
||||
/* Signal. */
|
||||
this->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
||||
|
||||
/* Sleep the thread. */
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lk(&timer, owner, timeout);
|
||||
KScopedSchedulerLockAndSleep lk(std::addressof(timer), owner, timeout);
|
||||
|
||||
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
||||
lk.CancelSleep();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user